rack-cache 1.2 → 1.3.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/test/options_test.rb DELETED
@@ -1,77 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
- require 'rack/cache/options'
3
-
4
- module Rack::Cache::Options
5
- option_accessor :foo
6
- end
7
-
8
- class MockOptions
9
- include Rack::Cache::Options
10
- def initialize
11
- @env = nil
12
- initialize_options
13
- end
14
- end
15
-
16
- describe 'Rack::Cache::Options' do
17
- before { @options = MockOptions.new }
18
-
19
- describe '#set' do
20
- it 'sets a Symbol option as rack-cache.symbol' do
21
- @options.set :bar, 'baz'
22
- @options.options['rack-cache.bar'].should.equal 'baz'
23
- end
24
- it 'sets a String option as string' do
25
- @options.set 'foo.bar', 'bling'
26
- @options.options['foo.bar'].should.equal 'bling'
27
- end
28
- it 'sets all key/value pairs when given a Hash' do
29
- @options.set :foo => 'bar', :bar => 'baz', 'foo.bar' => 'bling'
30
- @options.foo.should.equal 'bar'
31
- @options.options['rack-cache.bar'].should.equal 'baz'
32
- @options.options['foo.bar'].should.equal 'bling'
33
- end
34
- end
35
-
36
- it 'makes options declared with option_accessor available as attributes' do
37
- @options.set :foo, 'bar'
38
- @options.foo.should.equal 'bar'
39
- end
40
-
41
- it 'allows setting multiple options via assignment' do
42
- @options.options = { :foo => 'bar', :bar => 'baz', 'foo.bar' => 'bling' }
43
- @options.foo.should.equal 'bar'
44
- @options.options['foo.bar'].should.equal 'bling'
45
- @options.options['rack-cache.bar'].should.equal 'baz'
46
- end
47
-
48
- it "allows storing the value as a block" do
49
- block = Proc.new { "bar block" }
50
- @options.set(:foo, &block)
51
- @options.options['rack-cache.foo'].should.equal block
52
- end
53
-
54
- it 'allows the cache key generator to be configured' do
55
- @options.should.respond_to :cache_key
56
- @options.should.respond_to :cache_key=
57
- end
58
-
59
- it 'allows the meta store to be configured' do
60
- @options.should.respond_to :metastore
61
- @options.should.respond_to :metastore=
62
- @options.metastore.should.not.be.nil
63
- end
64
-
65
- it 'allows the entity store to be configured' do
66
- @options.should.respond_to :entitystore
67
- @options.should.respond_to :entitystore=
68
- @options.entitystore.should.not.be.nil
69
- end
70
-
71
- it 'allows log verbosity to be configured' do
72
- @options.should.respond_to :verbose
73
- @options.should.respond_to :verbose=
74
- @options.should.respond_to :verbose?
75
- @options.verbose.should.not.be.nil
76
- end
77
- end
data/test/pony.jpg DELETED
Binary file
data/test/request_test.rb DELETED
@@ -1,19 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
- require 'rack/cache/request'
3
-
4
- describe 'Rack::Cache::Request' do
5
- it 'is marked as no_cache when the Cache-Control header includes the no-cache directive' do
6
- request = Rack::Cache::Request.new('HTTP_CACHE_CONTROL' => 'public, no-cache')
7
- request.should.be.no_cache
8
- end
9
-
10
- it 'is marked as no_cache when request should not be loaded from cache' do
11
- request = Rack::Cache::Request.new('HTTP_PRAGMA' => 'no-cache')
12
- request.should.be.no_cache
13
- end
14
-
15
- it 'is not marked as no_cache when neither no-cache directive is specified' do
16
- request = Rack::Cache::Request.new('HTTP_CACHE_CONTROL' => 'public')
17
- request.should.not.be.no_cache
18
- end
19
- end
@@ -1,184 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
-
3
- describe 'Rack::Cache::Response' do
4
- before do
5
- @now = Time.httpdate(Time.now.httpdate)
6
- @one_hour_ago = Time.httpdate((Time.now - (60**2)).httpdate)
7
- @one_hour_later = Time.httpdate((Time.now + (60**2)).httpdate)
8
- @res = Rack::Cache::Response.new(200, {'Date' => @now.httpdate}, [])
9
- end
10
-
11
- after do
12
- @now, @res, @one_hour_ago = nil
13
- end
14
-
15
- it 'marks Rack tuples with string typed statuses as cacheable' do
16
- @res = Rack::Cache::Response.new('200',{'Date' => @now.httpdate},[])
17
- @res.headers['Expires'] = @one_hour_later.httpdate
18
- @res.should.be.cacheable
19
- end
20
-
21
- it 'responds to #to_a with a Rack response tuple' do
22
- @res.should.respond_to :to_a
23
- @res.to_a.should.equal [200, {'Date' => @now.httpdate}, []]
24
- end
25
-
26
- describe '#cache_control' do
27
- it 'handles multiple name=value pairs' do
28
- @res.headers['Cache-Control'] = 'max-age=600, max-stale=300, min-fresh=570'
29
- @res.cache_control['max-age'].should.equal '600'
30
- @res.cache_control['max-stale'].should.equal '300'
31
- @res.cache_control['min-fresh'].should.equal '570'
32
- end
33
- it 'removes the header when given an empty hash' do
34
- @res.headers['Cache-Control'] = 'max-age=600, must-revalidate'
35
- @res.cache_control['max-age'].should.equal '600'
36
- @res.cache_control = {}
37
- @res.headers.should.not.include 'Cache-Control'
38
- end
39
- end
40
-
41
- describe '#validateable?' do
42
- it 'is true when Last-Modified header present' do
43
- @res = Rack::Cache::Response.new(200, {'Last-Modified' => @one_hour_ago.httpdate}, [])
44
- @res.should.be.validateable
45
- end
46
- it 'is true when ETag header present' do
47
- @res = Rack::Cache::Response.new(200, {'ETag' => '"12345"'}, [])
48
- @res.should.be.validateable
49
- end
50
- it 'is false when no validator is present' do
51
- @res = Rack::Cache::Response.new(200, {}, [])
52
- @res.should.not.be.validateable
53
- end
54
- end
55
-
56
- describe '#date' do
57
- it 'uses the Date header if present' do
58
- @res = Rack::Cache::Response.new(200, {'Date' => @one_hour_ago.httpdate}, [])
59
- @res.date.should.equal @one_hour_ago
60
- end
61
- it 'uses the current time when no Date header present' do
62
- @res = Rack::Cache::Response.new(200, {}, [])
63
- @res.date.to_i.should.be.close Time.now.to_i, 1
64
- end
65
- it 'returns the correct date when the header is modified directly' do
66
- @res = Rack::Cache::Response.new(200, { 'Date' => @one_hour_ago.httpdate }, [])
67
- @res.date.should.equal @one_hour_ago
68
- @res.headers['Date'] = @now.httpdate
69
- @res.date.should.equal @now
70
- end
71
- end
72
-
73
- describe '#max_age' do
74
- it 'uses s-maxage cache control directive when present' do
75
- @res.headers['Cache-Control'] = 's-maxage=600, max-age=0'
76
- @res.max_age.should.equal 600
77
- end
78
- it 'falls back to max-age when no s-maxage directive present' do
79
- @res.headers['Cache-Control'] = 'max-age=600'
80
- @res.max_age.should.equal 600
81
- end
82
- it 'falls back to Expires when no max-age or s-maxage directive present' do
83
- @res.headers['Cache-Control'] = 'must-revalidate'
84
- @res.headers['Expires'] = @one_hour_later.httpdate
85
- @res.max_age.should.equal 60 ** 2
86
- end
87
- it 'gives a #max_age of nil when no freshness information available' do
88
- @res.max_age.should.be.nil
89
- end
90
- end
91
-
92
- describe '#private=' do
93
- it 'adds the private Cache-Control directive when set true' do
94
- @res.headers['Cache-Control'] = 'max-age=100'
95
- @res.private = true
96
- @res.headers['Cache-Control'].split(', ').sort.
97
- should.equal ['max-age=100', 'private']
98
- end
99
- it 'removes the public Cache-Control directive' do
100
- @res.headers['Cache-Control'] = 'public, max-age=100'
101
- @res.private = true
102
- @res.headers['Cache-Control'].split(', ').sort.
103
- should.equal ['max-age=100', 'private']
104
- end
105
- end
106
-
107
- describe '#expire!' do
108
- it 'sets the Age to be equal to the max-age' do
109
- @res.headers['Cache-Control'] = 'max-age=100'
110
- @res.expire!
111
- @res.headers['Age'].should.equal '100'
112
- end
113
- it 'sets the Age to be equal to the s-maxage when both max-age and s-maxage present' do
114
- @res.headers['Cache-Control'] = 'max-age=100, s-maxage=500'
115
- @res.expire!
116
- @res.headers['Age'].should.equal '500'
117
- end
118
- it 'does nothing when the response is already stale/expired' do
119
- @res.headers['Cache-Control'] = 'max-age=5, s-maxage=500'
120
- @res.headers['Age'] = '1000'
121
- @res.expire!
122
- @res.headers['Age'].should.equal '1000'
123
- end
124
- it 'does nothing when the response does not include freshness information' do
125
- @res.expire!
126
- @res.headers.should.not.include 'Age'
127
- end
128
- end
129
-
130
- describe '#ttl' do
131
- it 'is nil when no Expires or Cache-Control headers present' do
132
- @res.ttl.should.be.nil
133
- end
134
- it 'uses the Expires header when no max-age is present' do
135
- @res.headers['Expires'] = (@res.now + (60**2)).httpdate
136
- @res.ttl.should.be.close(60**2, 1)
137
- end
138
- it 'returns negative values when Expires is in part' do
139
- @res.ttl.should.be.nil
140
- @res.headers['Expires'] = @one_hour_ago.httpdate
141
- @res.ttl.should.be < 0
142
- end
143
- it 'uses the Cache-Control max-age value when present' do
144
- @res.headers['Cache-Control'] = 'max-age=60'
145
- @res.ttl.should.be.close(60, 1)
146
- end
147
- end
148
-
149
- describe '#vary' do
150
- it 'is nil when no Vary header is present' do
151
- @res.vary.should.be.nil
152
- end
153
- it 'returns the literal value of the Vary header' do
154
- @res.headers['Vary'] = 'Foo Bar Baz'
155
- @res.vary.should.equal 'Foo Bar Baz'
156
- end
157
- it 'can be checked for existence using the #vary? method' do
158
- @res.should.respond_to :vary?
159
- @res.should.not.vary
160
- @res.headers['Vary'] = '*'
161
- @res.should.vary
162
- end
163
- end
164
-
165
- describe '#vary_header_names' do
166
- it 'returns an empty Array when no Vary header is present' do
167
- @res.vary_header_names.should.be.empty
168
- end
169
- it 'parses a single header name value' do
170
- @res.headers['Vary'] = 'Accept-Language'
171
- @res.vary_header_names.should.equal ['Accept-Language']
172
- end
173
- it 'parses multiple header name values separated by spaces' do
174
- @res.headers['Vary'] = 'Accept-Language User-Agent X-Foo'
175
- @res.vary_header_names.should.equal \
176
- ['Accept-Language', 'User-Agent', 'X-Foo']
177
- end
178
- it 'parses multiple header name values separated by commas' do
179
- @res.headers['Vary'] = 'Accept-Language,User-Agent, X-Foo'
180
- @res.vary_header_names.should.equal \
181
- ['Accept-Language', 'User-Agent', 'X-Foo']
182
- end
183
- end
184
- end
data/test/spec_setup.rb DELETED
@@ -1,232 +0,0 @@
1
- require 'pp'
2
- require 'tmpdir'
3
- require 'stringio'
4
-
5
- [STDOUT, STDERR].each { |io| io.sync = true }
6
-
7
- begin
8
- require 'bacon'
9
- rescue LoadError => boom
10
- require 'rubygems' rescue nil
11
- require 'bacon'
12
- end
13
-
14
- # Set the MEMCACHED environment variable as follows to enable testing
15
- # of the MemCached meta and entity stores.
16
- ENV['MEMCACHED'] ||= 'localhost:11211'
17
- $memcached = nil
18
- $dalli = nil
19
-
20
- def have_memcached?(server=ENV['MEMCACHED'])
21
- return $memcached unless $memcached.nil?
22
-
23
- # silence warnings from memcached
24
- begin
25
- v, $VERBOSE = $VERBOSE, nil
26
- require 'memcached'
27
- ensure
28
- $VERBOSE = v
29
- end
30
-
31
- $memcached = Memcached.new(server)
32
- $memcached.set('ping', '')
33
- true
34
- rescue LoadError => boom
35
- warn "memcached library not available. related tests will be skipped."
36
- $memcached = false
37
- false
38
- rescue => boom
39
- warn "memcached not working. related tests will be skipped."
40
- $memcached = false
41
- false
42
- end
43
-
44
- have_memcached?
45
-
46
- def have_dalli?(server=ENV['MEMCACHED'])
47
- return $dalli unless $dalli.nil?
48
- require 'dalli'
49
- $dalli = Dalli::Client.new(server)
50
- $dalli.set('ping', '')
51
- true
52
- rescue LoadError => boom
53
- warn "dalli library not available. related tests will be skipped."
54
- $dalli = false
55
- false
56
- rescue => boom
57
- warn "dalli not working. related tests will be skipped."
58
- $dalli = false
59
- false
60
- end
61
-
62
- have_dalli?
63
-
64
- def need_dalli(forwhat)
65
- yield if have_dalli?
66
- end
67
-
68
- def need_memcached(forwhat)
69
- yield if have_memcached?
70
- end
71
-
72
- def need_java(forwhat)
73
- yield if RUBY_PLATFORM =~ /java/
74
- end
75
-
76
-
77
- # Setup the load path ..
78
- $LOAD_PATH.unshift File.dirname(File.dirname(__FILE__)) + '/lib'
79
- $LOAD_PATH.unshift File.dirname(__FILE__)
80
-
81
- require 'rack/cache'
82
-
83
- # Methods for constructing downstream applications / response
84
- # generators.
85
- module CacheContextHelpers
86
-
87
- # The Rack::Cache::Context instance used for the most recent
88
- # request.
89
- attr_reader :cache
90
-
91
- # An Array of Rack::Cache::Context instances used for each request, in
92
- # request order.
93
- attr_reader :caches
94
-
95
- # The Rack::Response instance result of the most recent request.
96
- attr_reader :response
97
-
98
- # An Array of Rack::Response instances for each request, in request order.
99
- attr_reader :responses
100
-
101
- # The backend application object.
102
- attr_reader :app
103
-
104
- def setup_cache_context
105
- # holds each Rack::Cache::Context
106
- @app = nil
107
-
108
- # each time a request is made, a clone of @cache_template is used
109
- # and appended to @caches.
110
- @cache_template = nil
111
- @cache = nil
112
- @caches = []
113
- @errors = StringIO.new
114
- @cache_config = nil
115
-
116
- @called = false
117
- @request = nil
118
- @response = nil
119
- @responses = []
120
-
121
- @storage = Rack::Cache::Storage.new
122
- end
123
-
124
- def teardown_cache_context
125
- @app, @cache_template, @cache, @caches, @called,
126
- @request, @response, @responses, @cache_config, @cache_prototype = nil
127
- end
128
-
129
- # A basic response with 200 status code and a tiny body.
130
- def respond_with(status=200, headers={}, body=['Hello World'], &bk)
131
- called = false
132
- @app =
133
- lambda do |env|
134
- called = true
135
- response = Rack::Response.new(body, status, headers)
136
- request = Rack::Request.new(env)
137
- bk.call(request, response) if bk
138
- response.finish
139
- end
140
- @app.meta_def(:called?) { called }
141
- @app.meta_def(:reset!) { called = false }
142
- @app
143
- end
144
-
145
- def cache_config(&block)
146
- @cache_config = block
147
- end
148
-
149
- def request(method, uri='/', opts={})
150
- opts = {
151
- 'rack.run_once' => true,
152
- 'rack.errors' => @errors,
153
- 'rack-cache.storage' => @storage
154
- }.merge(opts)
155
-
156
- fail 'response not specified (use respond_with)' if @app.nil?
157
- @app.reset! if @app.respond_to?(:reset!)
158
-
159
- @cache_prototype ||= Rack::Cache::Context.new(@app, &@cache_config)
160
- @cache = @cache_prototype.clone
161
- @caches << @cache
162
- @request = Rack::MockRequest.new(@cache)
163
- yield @cache if block_given?
164
- @response = @request.request(method.to_s.upcase, uri, opts)
165
- @responses << @response
166
- @response
167
- end
168
-
169
- def get(stem, env={}, &b)
170
- request(:get, stem, env, &b)
171
- end
172
-
173
- def head(stem, env={}, &b)
174
- request(:head, stem, env, &b)
175
- end
176
-
177
- def post(*args, &b)
178
- request(:post, *args, &b)
179
- end
180
- end
181
-
182
-
183
- module TestHelpers
184
- include FileUtils
185
- F = File
186
-
187
- @@temp_dir_count = 0
188
-
189
- def create_temp_directory
190
- @@temp_dir_count += 1
191
- path = F.join(Dir.tmpdir, "rack-cache-#{$$}-#{@@temp_dir_count}")
192
- mkdir_p path
193
- if block_given?
194
- yield path
195
- remove_entry_secure path
196
- end
197
- path
198
- end
199
-
200
- def create_temp_file(root, file, data='')
201
- path = F.join(root, file)
202
- mkdir_p F.dirname(path)
203
- F.open(path, 'w') { |io| io.write(data) }
204
- end
205
-
206
- end
207
-
208
- class Bacon::Context
209
- include TestHelpers
210
- include CacheContextHelpers
211
- end
212
-
213
- # Metaid == a few simple metaclass helper
214
- # (See http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html.)
215
- class Object
216
- # The hidden singleton lurks behind everyone
217
- def metaclass; class << self; self; end; end
218
- def meta_eval(&blk); metaclass.instance_eval(&blk); end
219
- # Adds methods to a metaclass
220
- def meta_def name, &blk
221
- meta_eval { define_method name, &blk }
222
- end
223
- # Defines an instance method within a class
224
- def class_def name, &blk
225
- class_eval { define_method name, &blk }
226
- end
227
-
228
- # True when the Object is neither false or nil.
229
- def truthy?
230
- !!self
231
- end
232
- end
data/test/storage_test.rb DELETED
@@ -1,94 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
- require 'rack/cache/storage'
3
-
4
- describe 'Rack::Cache::Storage' do
5
- before do
6
- @storage = Rack::Cache::Storage.new
7
- end
8
-
9
- it "fails when an unknown URI scheme is provided" do
10
- lambda { @storage.resolve_metastore_uri('foo:/') }.should.raise
11
- end
12
- it "creates a new MetaStore for URI if none exists" do
13
- @storage.resolve_metastore_uri('heap:/').
14
- should.be.kind_of Rack::Cache::MetaStore
15
- end
16
- it "returns an existing MetaStore instance for URI that exists" do
17
- store = @storage.resolve_metastore_uri('heap:/')
18
- @storage.resolve_metastore_uri('heap:/').should.be.same_as store
19
- end
20
- it "creates a new EntityStore for URI if none exists" do
21
- @storage.resolve_entitystore_uri('heap:/').
22
- should.be.kind_of Rack::Cache::EntityStore
23
- end
24
- it "returns an existing EntityStore instance for URI that exists" do
25
- store = @storage.resolve_entitystore_uri('heap:/')
26
- @storage.resolve_entitystore_uri('heap:/').should.be.same_as store
27
- end
28
- it "clears all URI -> store mappings with #clear" do
29
- meta = @storage.resolve_metastore_uri('heap:/')
30
- entity = @storage.resolve_entitystore_uri('heap:/')
31
- @storage.clear
32
- @storage.resolve_metastore_uri('heap:/').should.not.be.same_as meta
33
- @storage.resolve_entitystore_uri('heap:/').should.not.be.same_as entity
34
- end
35
-
36
- describe 'Heap Store URIs' do
37
- %w[heap:/ mem:/].each do |uri|
38
- it "resolves #{uri} meta store URIs" do
39
- @storage.resolve_metastore_uri(uri).
40
- should.be.kind_of Rack::Cache::MetaStore
41
- end
42
- it "resolves #{uri} entity store URIs" do
43
- @storage.resolve_entitystore_uri(uri).
44
- should.be.kind_of Rack::Cache::EntityStore
45
- end
46
- end
47
- end
48
-
49
- describe 'Disk Store URIs' do
50
- before do
51
- @temp_dir = create_temp_directory
52
- end
53
- after do
54
- remove_entry_secure @temp_dir
55
- @temp_dir = nil
56
- end
57
-
58
- %w[file: disk:].each do |uri|
59
- it "resolves #{uri} meta store URIs" do
60
- @storage.resolve_metastore_uri(uri + @temp_dir).
61
- should.be.kind_of Rack::Cache::MetaStore
62
- end
63
- it "resolves #{uri} entity store URIs" do
64
- @storage.resolve_entitystore_uri(uri + @temp_dir).
65
- should.be.kind_of Rack::Cache::EntityStore
66
- end
67
- end
68
- end
69
-
70
- if have_memcached?
71
-
72
- describe 'MemCache Store URIs' do
73
- %w[memcache: memcached:].each do |scheme|
74
- it "resolves #{scheme} meta store URIs" do
75
- uri = scheme + '//' + ENV['MEMCACHED']
76
- @storage.resolve_metastore_uri(uri).
77
- should.be.kind_of Rack::Cache::MetaStore
78
- end
79
- it "resolves #{scheme} entity store URIs" do
80
- uri = scheme + '//' + ENV['MEMCACHED']
81
- @storage.resolve_entitystore_uri(uri).
82
- should.be.kind_of Rack::Cache::EntityStore
83
- end
84
- end
85
- it 'supports namespaces in memcached: URIs' do
86
- uri = "memcached://" + ENV['MEMCACHED'] + "/namespace"
87
- @storage.resolve_metastore_uri(uri).
88
- should.be.kind_of Rack::Cache::MetaStore
89
- end
90
- end
91
-
92
- end
93
-
94
- end