rtomayko-rack-cache 0.3.9 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,4 +1,4 @@
1
- ## 0.4.0 / Unreleased
1
+ ## 0.4.0 / March 2009
2
2
 
3
3
  * Ruby 1.9.1 / Rack 1.0 compatible.
4
4
 
@@ -9,7 +9,8 @@
9
9
 
10
10
  * Implement "Cache-Control: max-age=N" request directive by forcing
11
11
  validation when the max-age provided exceeds the age of the cache
12
- entry.
12
+ entry. This can be disabled by setting the "allow_revalidate" option to
13
+ false.
13
14
 
14
15
  * Properly implement "Cache-Control: no-cache" request directive by
15
16
  performing a full reload. RFC 2616 states that when "no-cache" is
@@ -19,7 +20,8 @@
19
20
  first validate its entry with the origin. Previously, we implemented
20
21
  "no-cache" on requests by passing so no new cache entry would be stored
21
22
  based on the response. Now we treat it as a forced miss and enter the
22
- response into the cache if it's cacheable.
23
+ response into the cache if it's cacheable. This can be disabled by
24
+ setting the "allow_reload" option to false.
23
25
 
24
26
  * Assume identical semantics for the "Pragma: no-cache" request header
25
27
  as the "Cache-Control: no-cache" directive described above.
data/README CHANGED
@@ -24,14 +24,6 @@ caching solution for small to medium sized deployments. More sophisticated /
24
24
  high-performance caching systems (e.g., Varnish, Squid, httpd/mod-cache) may be
25
25
  more appropriate for large deployments with significant throughput requirements.
26
26
 
27
- Status
28
- ------
29
-
30
- Rack::Cache is a young and experimental project that is likely to change
31
- substantially and may not be wholly functional, consistent, fast, or correct.
32
- The current focus is on reaching basic compliance with RFC 2616 and providing
33
- good documentation.
34
-
35
27
  Installation
36
28
  ------------
37
29
 
@@ -65,6 +57,24 @@ Assuming you've designed your backend application to take advantage of HTTP's
65
57
  caching features, no further code or configuration is required for basic
66
58
  caching.
67
59
 
60
+ Using with Rails
61
+ ----------------
62
+
63
+ Add this to your `config/environment.rb`:
64
+
65
+ config.middleware.use Rack::Cache,
66
+ :verbose => true,
67
+ :metastore => 'file:/var/cache/rack/meta',
68
+ :entitystore => 'file:/var/cache/rack/body'
69
+
70
+ You should now see `Rack::Cache` listed in the middleware pipeline:
71
+
72
+ rake middleware
73
+
74
+ See the following for more information:
75
+
76
+ http://snippets.aktagon.com/snippets/302
77
+
68
78
  Links
69
79
  -----
70
80
 
data/Rakefile CHANGED
@@ -31,7 +31,7 @@ end
31
31
 
32
32
  # DOC =======================================================================
33
33
  desc 'Build all documentation'
34
- task :doc => %w[doc:api doc:graphs doc:markdown]
34
+ task :doc => %w[doc:api doc:markdown]
35
35
 
36
36
  # requires the hanna gem:
37
37
  # gem install mislav-hanna --source=http://gems.github.com
@@ -55,19 +55,6 @@ file 'doc/api/index.html' => FileList['lib/**/*.rb'] do |f|
55
55
  end
56
56
  CLEAN.include 'doc/api'
57
57
 
58
- desc 'Build graphviz graphs'
59
- task 'doc:graphs'
60
- %w[pdf png svg].each do |filetype|
61
- FileList["doc/*.dot"].each do |source|
62
- dest = source.sub(/dot$/, filetype)
63
- file dest => source do |f|
64
- sh "dot -T#{filetype} #{source} -o #{f.name}"
65
- end
66
- task 'doc:graphs' => dest
67
- CLEAN.include dest
68
- end
69
- end
70
-
71
58
  desc 'Build markdown documentation files'
72
59
  task 'doc:markdown'
73
60
  FileList['doc/*.markdown'].each do |source|
data/TODO CHANGED
@@ -1,13 +1,8 @@
1
- ## 0.4
1
+ ## Backlog
2
2
 
3
3
  - Move breakers.rb configuration file into rack-contrib as a middleware
4
4
  component.
5
- - Add docs on using Rack::Cache with Rails 2.3 or link to one of the
6
- existing tutorials on this.
7
5
  - Sample apps: Rack, Rails, Sinatra, Merb, etc.
8
-
9
- ## Backlog
10
-
11
6
  - Use Bacon instead of test/spec
12
7
  - Work with both memcache and memcached gems (memcached hasn't built on MacOS
13
8
  for some time now).
data/doc/faq.markdown CHANGED
@@ -10,6 +10,14 @@ General
10
10
  -------
11
11
 
12
12
 
13
+ <a class='hash' id='rails' href='#rails'>#</a>
14
+
15
+ ### Q: Can I use Rack::Cache with Rails?
16
+
17
+ Rack::Cache can be used with Rails 2.3 or above. Documentation and a
18
+ sample application is forthcoming; in the mean time, see
19
+ [this example of using Rack::Cache with Rails 2.3](http://snippets.aktagon.com/snippets/302-How-to-setup-and-use-Rack-Cache-with-Rails-2-3-0-RC-1).
20
+
13
21
  <a class='hash' id='why-not-squid' href='#why-not-squid'>#</a>
14
22
 
15
23
  ### Q: Why Rack::Cache? Why not Squid, Varnish, Perlbol, etc.?
data/doc/index.markdown CHANGED
@@ -9,13 +9,13 @@ for [Rack][]-based applications that produce freshness (`Expires`,
9
9
  * Portable: 100% Ruby / works with any [Rack][]-enabled framework.
10
10
  * Disk, memcached, and heap memory [storage backends][storage].
11
11
 
12
- Status
13
- ------
12
+ News
13
+ ----
14
14
 
15
- __Rack::Cache__ is a young and experimental project that is likely to
16
- change substantially and may not be wholly functional, consistent,
17
- fast, or correct. The current focus is on reaching basic compliance
18
- with RFC 2616 and providing good documentation.
15
+ * [How to use Rack::Cache with Rails 2.3](http://snippets.aktagon.com/snippets/302-How-to-setup-and-use-Rack-Cache-with-Rails-2-3-0-RC-1) - it's really easy.
16
+ * [RailsLab's Advanced HTTP Caching Screencast](http://railslab.newrelic.com/2009/02/26/episode-11-advanced-http-caching)
17
+ is a really great review of HTTP caching concepts and shows how to
18
+ use Rack::Cache with Rails.
19
19
 
20
20
  Installation
21
21
  ------------
@@ -51,7 +51,7 @@ caching.
51
51
  More
52
52
  ----
53
53
 
54
- * [Configuration and Options][config] - how to set cache options.
54
+ * [Configuration Options][config] - how to set cache options.
55
55
 
56
56
  * [Cache Storage Documentation][storage] - detailed information on the various
57
57
  storage implementations available in __Rack::Cache__ and how to choose the one
@@ -15,11 +15,12 @@ module Rack::Cache
15
15
  # The Rack application object immediately downstream.
16
16
  attr_reader :backend
17
17
 
18
- def initialize(backend, options={}, &block)
18
+ def initialize(backend, options={})
19
19
  @backend = backend
20
20
  @trace = []
21
+
21
22
  initialize_options options
22
- instance_eval(&block) if block_given?
23
+ yield self if block_given?
23
24
 
24
25
  @private_header_keys =
25
26
  private_headers.map { |name| "HTTP_#{name.upcase.tr('-', '_')}" }
@@ -109,7 +110,7 @@ module Rack::Cache
109
110
  # Whether the cache entry is "fresh enough" to satisfy the request.
110
111
  def fresh_enough?(entry)
111
112
  if entry.fresh?
112
- if max_age = @request.cache_control.max_age
113
+ if allow_revalidate? && max_age = @request.cache_control.max_age
113
114
  max_age > 0 && max_age >= entry.age
114
115
  else
115
116
  true
@@ -143,7 +144,7 @@ module Rack::Cache
143
144
  # stale, attempt to #validate the entry with the backend using conditional
144
145
  # GET. When no matching cache entry is found, trigger #miss processing.
145
146
  def lookup
146
- if @request.no_cache?
147
+ if @request.no_cache? && allow_reload?
147
148
  record :reload
148
149
  fetch
149
150
  elsif entry = metastore.lookup(@request, entitystore)
@@ -7,20 +7,22 @@ module Rack::Cache
7
7
  # uses the Rack Environment to store option values. All options documented
8
8
  # below are stored in the Rack Environment as "rack-cache.<option>", where
9
9
  # <option> is the option name.
10
- #
11
- # The #set method can be used to configure a option values. When #set is
12
- # called outside of request scope, the value applies to all requests; when
13
- # called from within a request context, applies only to the request being
14
- # processed.
15
10
  module Options
16
- class << self
17
- private
18
- def option_accessor(key)
19
- define_method(key) { || read_option(key) }
20
- define_method("#{key}=") { |value| write_option(key, value) }
21
- define_method("#{key}?") { || !! read_option(key) }
11
+ def self.option_accessor(key)
12
+ name = option_name(key)
13
+ define_method(key) { || options[name] }
14
+ define_method("#{key}=") { |value| options[name] = value }
15
+ define_method("#{key}?") { || !! options[name] }
16
+ end
17
+
18
+ def option_name(key)
19
+ case key
20
+ when Symbol ; "rack-cache.#{key}"
21
+ when String ; key
22
+ else raise ArgumentError
22
23
  end
23
24
  end
25
+ module_function :option_name
24
26
 
25
27
  # Enable verbose trace logging. This option is currently enabled by
26
28
  # default but is likely to be disabled in a future release.
@@ -57,9 +59,9 @@ module Rack::Cache
57
59
  # end
58
60
  option_accessor :cache_key
59
61
 
60
- # A URI specifying the entity-store implement that should be used to store
61
- # response bodies. See the metastore option for information on supported URI
62
- # schemes.
62
+ # A URI specifying the entity-store implementation that should be used to
63
+ # store response bodies. See the metastore option for information on
64
+ # supported URI schemes.
63
65
  #
64
66
  # If no entity store is specified the 'heap:/' store is assumed. This
65
67
  # implementation has significant draw-backs so explicit configuration is
@@ -83,6 +85,16 @@ module Rack::Cache
83
85
  # Default: ['Authorization', 'Cookie']
84
86
  option_accessor :private_headers
85
87
 
88
+ # Specifies whether the client can force a cache reload by including a
89
+ # Cache-Control "no-cache" directive in the request. This is enabled by
90
+ # default for compliance with RFC 2616.
91
+ option_accessor :allow_reload
92
+
93
+ # Specifies whether the client can force a cache revalidate by including
94
+ # a Cache-Control "max-age=0" directive in the request. This is enabled by
95
+ # default for compliance with RFC 2616.
96
+ option_accessor :allow_revalidate
97
+
86
98
  # The underlying options Hash. During initialization (or outside of a
87
99
  # request), this is a default values Hash. During a request, this is the
88
100
  # Rack environment Hash. The default values Hash is merged in underneath
@@ -112,6 +124,21 @@ module Rack::Cache
112
124
  end
113
125
 
114
126
  private
127
+ def initialize_options(options={})
128
+ @default_options = {
129
+ 'rack-cache.cache_key' => Key,
130
+ 'rack-cache.verbose' => true,
131
+ 'rack-cache.storage' => Rack::Cache::Storage.instance,
132
+ 'rack-cache.metastore' => 'heap:/',
133
+ 'rack-cache.entitystore' => 'heap:/',
134
+ 'rack-cache.default_ttl' => 0,
135
+ 'rack-cache.private_headers' => ['Authorization', 'Cookie'],
136
+ 'rack-cache.allow_reload' => true,
137
+ 'rack-cache.allow_revalidate' => true
138
+ }
139
+ self.options = options
140
+ end
141
+
115
142
  def read_option(key)
116
143
  options[option_name(key)]
117
144
  end
@@ -119,27 +146,5 @@ module Rack::Cache
119
146
  def write_option(key, value)
120
147
  options[option_name(key)] = value
121
148
  end
122
-
123
- def option_name(key)
124
- case key
125
- when Symbol ; "rack-cache.#{key}"
126
- when String ; key
127
- else raise ArgumentError
128
- end
129
- end
130
-
131
- private
132
- def initialize_options(options={})
133
- @default_options = {
134
- 'rack-cache.cache_key' => Key,
135
- 'rack-cache.verbose' => true,
136
- 'rack-cache.storage' => Rack::Cache::Storage.instance,
137
- 'rack-cache.metastore' => 'heap:/',
138
- 'rack-cache.entitystore' => 'heap:/',
139
- 'rack-cache.default_ttl' => 0,
140
- 'rack-cache.private_headers' => ['Authorization', 'Cookie']
141
- }
142
- self.options = options
143
- end
144
149
  end
145
150
  end
data/rack-cache.gemspec CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
3
3
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
4
 
5
5
  s.name = 'rack-cache'
6
- s.version = '0.3.9'
7
- s.date = '2009-03-07'
6
+ s.version = '0.4'
7
+ s.date = '2009-03-16'
8
8
 
9
9
  s.description = "HTTP Caching for Rack"
10
10
  s.summary = "HTTP Caching for Rack"
@@ -20,7 +20,6 @@ Gem::Specification.new do |s|
20
20
  Rakefile
21
21
  TODO
22
22
  doc/configuration.markdown
23
- doc/events.dot
24
23
  doc/faq.markdown
25
24
  doc/index.markdown
26
25
  doc/layout.html.erb
data/test/cache_test.rb CHANGED
@@ -25,14 +25,14 @@ describe 'Rack::Cache::new' do
25
25
  end
26
26
 
27
27
  it 'takes a block; executes it during initialization' do
28
- state, block_scope = 'not invoked', nil
29
- object =
30
- Rack::Cache.new @app do
31
- block_scope = self
28
+ state, object = 'not invoked', nil
29
+ instance =
30
+ Rack::Cache.new @app do |cache|
31
+ object = cache
32
32
  state = 'invoked'
33
- should.respond_to :set
33
+ cache.should.respond_to :set
34
34
  end
35
35
  state.should.equal 'invoked'
36
- object.should.be block_scope
36
+ object.should.be instance
37
37
  end
38
38
  end
data/test/context_test.rb CHANGED
@@ -148,6 +148,31 @@ describe 'Rack::Cache::Context' do
148
148
  cache.trace.should.include :store
149
149
  end
150
150
 
151
+ it 'does not reload responses when allow_reload is set false' do
152
+ count = 0
153
+ respond_with 200, 'Cache-Control' => 'max-age=10000' do |req,res|
154
+ count+= 1
155
+ res.body = (count == 1) ? ['Hello World'] : ['Goodbye World']
156
+ end
157
+
158
+ get '/'
159
+ response.should.be.ok
160
+ response.body.should.equal 'Hello World'
161
+ cache.trace.should.include :store
162
+
163
+ get '/'
164
+ response.should.be.ok
165
+ response.body.should.equal 'Hello World'
166
+ cache.trace.should.include :fresh
167
+
168
+ get '/',
169
+ 'rack-cache.allow_reload' => false,
170
+ 'HTTP_CACHE_CONTROL' => 'no-cache'
171
+ response.should.be.ok
172
+ response.body.should.equal 'Hello World'
173
+ cache.trace.should.not.include :reload
174
+ end
175
+
151
176
  it 'revalidates fresh cache entry when max-age request directive is exceeded' do
152
177
  count = 0
153
178
  respond_with do |req,res|
@@ -175,6 +200,34 @@ describe 'Rack::Cache::Context' do
175
200
  cache.trace.should.include :store
176
201
  end
177
202
 
203
+ it 'does not revalidate fresh cache entry when enable_revalidate option is set false' do
204
+ count = 0
205
+ respond_with do |req,res|
206
+ count+= 1
207
+ res['Cache-Control'] = 'max-age=10000'
208
+ res['ETag'] = count.to_s
209
+ res.body = (count == 1) ? ['Hello World'] : ['Goodbye World']
210
+ end
211
+
212
+ get '/'
213
+ response.should.be.ok
214
+ response.body.should.equal 'Hello World'
215
+ cache.trace.should.include :store
216
+
217
+ get '/'
218
+ response.should.be.ok
219
+ response.body.should.equal 'Hello World'
220
+ cache.trace.should.include :fresh
221
+
222
+ get '/',
223
+ 'rack-cache.allow_revalidate' => false,
224
+ 'HTTP_CACHE_CONTROL' => 'max-age=0'
225
+ response.should.be.ok
226
+ response.body.should.equal 'Hello World'
227
+ cache.trace.should.not.include :stale
228
+ cache.trace.should.not.include :invalid
229
+ cache.trace.should.include :fresh
230
+ end
178
231
  it 'fetches response from backend when cache misses' do
179
232
  respond_with 200, 'Expires' => (Time.now + 5).httpdate
180
233
  get '/'
data/test/spec_setup.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'pp'
2
2
  require 'tmpdir'
3
+ require 'stringio'
3
4
 
4
5
  [ STDOUT, STDERR ].each { |io| io.sync = true }
5
6
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rtomayko-rack-cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.9
4
+ version: "0.4"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Tomayko
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-07 00:00:00 -08:00
12
+ date: 2009-03-16 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -40,7 +40,6 @@ files:
40
40
  - Rakefile
41
41
  - TODO
42
42
  - doc/configuration.markdown
43
- - doc/events.dot
44
43
  - doc/faq.markdown
45
44
  - doc/index.markdown
46
45
  - doc/layout.html.erb
data/doc/events.dot DELETED
@@ -1,27 +0,0 @@
1
- digraph cache_logic {
2
- nodesep=1.25;
3
- center=true;
4
-
5
- node[fontname="Lucida Sans Unicode",labelloc=c,margin=0.10,0.03]
6
- edge[fontname="Lucida Sans Unicode",fontcolor="#444444",labeldistance=20];
7
-
8
- receive -> pass[label="uncacheable request",color=grey];
9
- receive -> lookup[label="cacheable request"];
10
-
11
- pass -> deliver[label="",color=grey];
12
-
13
- lookup -> hit[label="fresh"];
14
- lookup -> fetch[label="stale (needs validation)"];
15
- lookup -> miss[label="uncached"];
16
-
17
- hit -> deliver[label="sizzling"];
18
- hit -> pass[label="bailing...",color=grey];
19
-
20
- miss -> fetch[label=""];
21
- miss -> pass[color=grey];
22
-
23
- fetch -> store[label="cacheable"];
24
- fetch -> deliver[label="not cacheable",color=grey];
25
-
26
- store -> deliver[label="KTHX"];
27
- }