rack-cache 0.2.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.
Potentially problematic release.
This version of rack-cache might be problematic. Click here for more details.
- data/CHANGES +27 -0
- data/COPYING +18 -0
- data/README +96 -0
- data/Rakefile +144 -0
- data/TODO +40 -0
- data/doc/configuration.markdown +224 -0
- data/doc/events.dot +27 -0
- data/doc/faq.markdown +133 -0
- data/doc/index.markdown +113 -0
- data/doc/layout.html.erb +33 -0
- data/doc/license.markdown +24 -0
- data/doc/rack-cache.css +362 -0
- data/doc/storage.markdown +162 -0
- data/lib/rack/cache.rb +51 -0
- data/lib/rack/cache/config.rb +65 -0
- data/lib/rack/cache/config/busters.rb +16 -0
- data/lib/rack/cache/config/default.rb +134 -0
- data/lib/rack/cache/config/no-cache.rb +13 -0
- data/lib/rack/cache/context.rb +95 -0
- data/lib/rack/cache/core.rb +271 -0
- data/lib/rack/cache/entitystore.rb +224 -0
- data/lib/rack/cache/headers.rb +237 -0
- data/lib/rack/cache/metastore.rb +309 -0
- data/lib/rack/cache/options.rb +119 -0
- data/lib/rack/cache/request.rb +37 -0
- data/lib/rack/cache/response.rb +76 -0
- data/lib/rack/cache/storage.rb +50 -0
- data/lib/rack/utils/environment_headers.rb +78 -0
- data/rack-cache.gemspec +74 -0
- data/test/cache_test.rb +35 -0
- data/test/config_test.rb +66 -0
- data/test/context_test.rb +465 -0
- data/test/core_test.rb +84 -0
- data/test/entitystore_test.rb +176 -0
- data/test/environment_headers_test.rb +71 -0
- data/test/headers_test.rb +215 -0
- data/test/logging_test.rb +45 -0
- data/test/metastore_test.rb +210 -0
- data/test/options_test.rb +64 -0
- data/test/pony.jpg +0 -0
- data/test/response_test.rb +37 -0
- data/test/spec_setup.rb +189 -0
- data/test/storage_test.rb +94 -0
- metadata +120 -0
data/CHANGES
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
## 0.2.0 / 2008-10-24 / Initial Release
|
2
|
+
|
3
|
+
* Document events and transitions in `rack/cache/config/default.rb`
|
4
|
+
* Basic logging support (`trace`, `warn`, `info`, `error` from within Context)
|
5
|
+
* EntityStore: store entity bodies keyed by SHA
|
6
|
+
* MetaStore: store response headers keyed by URL
|
7
|
+
* Last-Modified/ETag validation
|
8
|
+
* Vary support
|
9
|
+
* Implement error! transition
|
10
|
+
* New Rack::Cache::Core
|
11
|
+
* memcached meta and entity store implementations
|
12
|
+
* URI based storage configuration
|
13
|
+
* Read options from Rack env if present (rack-cache.XXX keys)
|
14
|
+
* `object` is now `entry`
|
15
|
+
* Documentation framework and website
|
16
|
+
* Document storage areas and implementations
|
17
|
+
* Document configuration/events
|
18
|
+
|
19
|
+
## 0.1.0 / 2008-07-21 / Proof of concept (unreleased)
|
20
|
+
|
21
|
+
* Basic core with event support
|
22
|
+
* `#import` method for bringing in config files
|
23
|
+
* Freshness based expiration
|
24
|
+
* RFC 2616 If-Modified-Since based validation
|
25
|
+
* A horribly shitty storage back-end (Hash in mem)
|
26
|
+
* Don't cache hop-by-hop headers: Connection, Keep-Alive, Proxy-Authenticate,
|
27
|
+
Proxy-Authorization, TE, Trailers, Transfer-Encoding, Upgrade
|
data/COPYING
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2008 Ryan Tomayko <http://tomayko.com/about>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal in the Software without restriction, including without limitation the
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
Rack::Cache
|
2
|
+
===========
|
3
|
+
|
4
|
+
Rack::Cache is suitable as a quick drop-in component to enable HTTP caching for
|
5
|
+
Rack-based applications that produce freshness (Expires, Cache-Control) and/or
|
6
|
+
validation (Last-Modified, ETag) information.
|
7
|
+
|
8
|
+
We strive to implement those portions of RFC 2616 Section 13 relevant to gateway
|
9
|
+
(i.e., "reverse proxy") cache scenarios with a system for specifying cache
|
10
|
+
policy:
|
11
|
+
|
12
|
+
* Standards-based (RFC 2616)
|
13
|
+
* Freshness/expiration based caching
|
14
|
+
* Validation
|
15
|
+
* Vary support
|
16
|
+
* Portable: 100% Ruby / works with any Rack-enabled framework
|
17
|
+
* Configuration language for advanced caching policies
|
18
|
+
* Disk, memcached, and heap memory storage backends
|
19
|
+
|
20
|
+
For more information about Rack::Cache features and usage, see:
|
21
|
+
|
22
|
+
http://tomayko.com/src/rack-cache/
|
23
|
+
|
24
|
+
Rack::Cache is not overly optimized for performance. The main goal of the
|
25
|
+
project is to provide a portable, easy-to-configure, and standards-based
|
26
|
+
caching solution for small to medium sized deployments. More sophisticated /
|
27
|
+
high-performance caching systems (e.g., Varnish, Squid, httpd/mod-cache) may be
|
28
|
+
more appropriate for large deployments with significant throughput requirements.
|
29
|
+
|
30
|
+
Status
|
31
|
+
------
|
32
|
+
|
33
|
+
Rack::Cache is a young and experimental project that is likely to change
|
34
|
+
substantially and may not be wholly functional, consistent, fast, or correct.
|
35
|
+
The current focus is on reaching basic compliance with RFC 2616 and providing
|
36
|
+
good documentation.
|
37
|
+
|
38
|
+
Installation
|
39
|
+
------------
|
40
|
+
|
41
|
+
From Gem:
|
42
|
+
|
43
|
+
$ sudo gem install rack-cache
|
44
|
+
|
45
|
+
With a local working copy:
|
46
|
+
|
47
|
+
$ git clone git://github.com/rtomayko/rack-cache.git
|
48
|
+
$ rake package && sudo rake install
|
49
|
+
|
50
|
+
Basic Usage
|
51
|
+
-----------
|
52
|
+
|
53
|
+
Rack::Cache is implemented as a piece of Rack middleware and can be used with
|
54
|
+
any Rack-based application. If your application includes a rackup (`.ru`) file
|
55
|
+
or uses Rack::Builder to construct the application pipeline, simply require
|
56
|
+
and use as follows:
|
57
|
+
|
58
|
+
require 'rack/cache'
|
59
|
+
|
60
|
+
use Rack::Cache,
|
61
|
+
:metastore => 'file:/var/cache/rack/meta',
|
62
|
+
:entitystore => 'file:/var/cache/rack/body',
|
63
|
+
:verbose => true
|
64
|
+
|
65
|
+
run app
|
66
|
+
|
67
|
+
Assuming you've designed your backend application to take advantage of HTTP's
|
68
|
+
caching features, no further code or configuration is required for basic
|
69
|
+
caching.
|
70
|
+
|
71
|
+
Documentation
|
72
|
+
-------------
|
73
|
+
|
74
|
+
http://tomayko.com/src/rack-cache/
|
75
|
+
|
76
|
+
License
|
77
|
+
-------
|
78
|
+
|
79
|
+
Copyright (c) 2008 Ryan Tomayko <http://tomayko.com/about>
|
80
|
+
|
81
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
82
|
+
of this software and associated documentation files (the "Software"), to
|
83
|
+
deal in the Software without restriction, including without limitation the
|
84
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
85
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
86
|
+
furnished to do so, subject to the following conditions:
|
87
|
+
|
88
|
+
The above copyright notice and this permission notice shall be included in
|
89
|
+
all copies or substantial portions of the Software.
|
90
|
+
|
91
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
92
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
93
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
94
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
95
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
96
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
|
3
|
+
task :default => :test
|
4
|
+
|
5
|
+
CLEAN.include %w[coverage/ doc/api tags]
|
6
|
+
CLOBBER.include %w[dist]
|
7
|
+
|
8
|
+
# load gemspec like github's gem builder to surface any SAFE issues.
|
9
|
+
Thread.new do
|
10
|
+
require 'rubygems/specification'
|
11
|
+
$spec = eval("$SAFE=3\n#{File.read('rack-cache.gemspec')}")
|
12
|
+
end.join
|
13
|
+
|
14
|
+
# SPECS =====================================================================
|
15
|
+
|
16
|
+
desc 'Run specs with story style output'
|
17
|
+
task :spec do
|
18
|
+
sh 'specrb --specdox -Ilib:test test/*_test.rb'
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'Run specs with unit test style output'
|
22
|
+
task :test => FileList['test/*_test.rb'] do |t|
|
23
|
+
suite = t.prerequisites
|
24
|
+
sh "testrb -Ilib:test #{suite.join(' ')}", :verbose => false
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Generate test coverage report'
|
28
|
+
task :rcov do
|
29
|
+
sh "rcov -Ilib:test test/*_test.rb"
|
30
|
+
end
|
31
|
+
|
32
|
+
# DOC =======================================================================
|
33
|
+
desc 'Build all documentation'
|
34
|
+
task :doc => %w[doc:api doc:graphs doc:markdown]
|
35
|
+
|
36
|
+
# requires the hanna gem:
|
37
|
+
# gem install mislav-hanna --source=http://gems.github.com
|
38
|
+
desc 'Build API documentation (doc/api)'
|
39
|
+
task 'doc:api' => 'doc/api/index.html'
|
40
|
+
file 'doc/api/index.html' => FileList['lib/**/*.rb'] do |f|
|
41
|
+
rm_rf 'doc/api'
|
42
|
+
sh((<<-SH).gsub(/[\s\n]+/, ' ').strip)
|
43
|
+
hanna
|
44
|
+
--op doc/api
|
45
|
+
--promiscuous
|
46
|
+
--charset utf8
|
47
|
+
--fmt html
|
48
|
+
--inline-source
|
49
|
+
--line-numbers
|
50
|
+
--accessor option_accessor=RW
|
51
|
+
--main Rack::Cache
|
52
|
+
--title 'Rack::Cache API Documentation'
|
53
|
+
#{f.prerequisites.join(' ')}
|
54
|
+
SH
|
55
|
+
end
|
56
|
+
CLEAN.include 'doc/api'
|
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
|
+
desc 'Build markdown documentation files'
|
72
|
+
task 'doc:markdown'
|
73
|
+
FileList['doc/*.markdown'].each do |source|
|
74
|
+
dest = "doc/#{File.basename(source, '.markdown')}.html"
|
75
|
+
file dest => source do |f|
|
76
|
+
puts "markdown: #{source} -> #{dest}" if verbose
|
77
|
+
require 'erb'
|
78
|
+
require 'rdiscount'
|
79
|
+
template = File.read(source)
|
80
|
+
content = Markdown.new(ERB.new(template, 0, "%<>").result(binding)).to_html
|
81
|
+
title = content.match("<h1>(.*)</h1>")[1] rescue ''
|
82
|
+
layout = ERB.new(File.read("doc/layout.html.erb"), 0, "%<>")
|
83
|
+
output = layout.result(binding)
|
84
|
+
File.open(dest, 'w') { |io| io.write(output) }
|
85
|
+
end
|
86
|
+
task 'doc:markdown' => dest
|
87
|
+
CLEAN.include dest
|
88
|
+
end
|
89
|
+
|
90
|
+
task 'doc:publish' => :doc do
|
91
|
+
sh 'rsync -avz doc/ gus@tomayko.com:/src/rack-cache'
|
92
|
+
end
|
93
|
+
|
94
|
+
# PACKAGING =================================================================
|
95
|
+
|
96
|
+
def package(ext='')
|
97
|
+
"dist/rack-cache-#{$spec.version}" + ext
|
98
|
+
end
|
99
|
+
|
100
|
+
desc 'Build packages'
|
101
|
+
task :package => %w[.gem .tar.gz].map {|e| package(e)}
|
102
|
+
|
103
|
+
desc 'Build and install as local gem'
|
104
|
+
task :install => package('.gem') do
|
105
|
+
sh "gem install #{package('.gem')}"
|
106
|
+
end
|
107
|
+
|
108
|
+
directory 'dist/'
|
109
|
+
|
110
|
+
file package('.gem') => %w[dist/ rack-cache.gemspec] + $spec.files do |f|
|
111
|
+
sh "gem build rack-cache.gemspec"
|
112
|
+
mv File.basename(f.name), f.name
|
113
|
+
end
|
114
|
+
|
115
|
+
file package('.tar.gz') => %w[dist/] + $spec.files do |f|
|
116
|
+
sh "git archive --format=tar HEAD | gzip > #{f.name}"
|
117
|
+
end
|
118
|
+
|
119
|
+
desc 'Upload gem and tar.gz distributables to rubyforge'
|
120
|
+
task :release => [package('.gem'), package('.tar.gz')] do |t|
|
121
|
+
sh <<-SH
|
122
|
+
rubyforge add_release wink rack-cache #{$spec.version} #{package('.gem')} &&
|
123
|
+
rubyforge add_file wink rack-cache #{$spec.version} #{package('.tar.gz')}
|
124
|
+
SH
|
125
|
+
end
|
126
|
+
|
127
|
+
# GEMSPEC ===================================================================
|
128
|
+
|
129
|
+
file 'rack-cache.gemspec' => FileList['{lib,test}/**','Rakefile'] do |f|
|
130
|
+
# read spec file and split out manifest section
|
131
|
+
spec = File.read(f.name)
|
132
|
+
parts = spec.split(" # = MANIFEST =\n")
|
133
|
+
fail 'bad spec' if parts.length != 3
|
134
|
+
# determine file list from git ls-files
|
135
|
+
files = `git ls-files`.
|
136
|
+
split("\n").sort.reject{ |file| file =~ /^\./ }.
|
137
|
+
map{ |file| " #{file}" }.join("\n")
|
138
|
+
# piece file back together and write...
|
139
|
+
parts[1] = " s.files = %w[\n#{files}\n ]\n"
|
140
|
+
spec = parts.join(" # = MANIFEST =\n")
|
141
|
+
spec.sub!(/s.date = '.*'/, "s.date = '#{Time.now.strftime("%Y-%m-%d")}'")
|
142
|
+
File.open(f.name, 'w') { |io| io.write(spec) }
|
143
|
+
puts "updated #{f.name}"
|
144
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
## 0.3
|
2
|
+
|
3
|
+
- BUG: meta store hits but entity misses
|
4
|
+
- BUG: HEAD request on invalid entry caches zero-length response
|
5
|
+
- BUG: Response body written to cache each time validation succeeds
|
6
|
+
- Are we doing HEAD properly?
|
7
|
+
- liberal, conservative, sane caching configs
|
8
|
+
- Sample app
|
9
|
+
- busters.rb doc and tests
|
10
|
+
- no-cache.rb doc and tests
|
11
|
+
- Canonicalized URL for cache key:
|
12
|
+
- sorts params by key, then value
|
13
|
+
- urlencodes /[^ A-Za-z0-9_.-]/ host, path, and param key/value
|
14
|
+
- Support server-specific X-Sendfile (or similar) for delivering cached
|
15
|
+
bodies (file entity stores only).
|
16
|
+
- Sqlite3 (meta store)
|
17
|
+
- Cache invalidation on PUT, POST, DELETE.
|
18
|
+
- Invalidate at the request URI; or, anything that's "near" the request URI.
|
19
|
+
- Invalidate at the URI of the Location or Content-Location response header.
|
20
|
+
|
21
|
+
## Backlog
|
22
|
+
|
23
|
+
- Purge/invalidate specific cache entries
|
24
|
+
- Purge/invalidate everything
|
25
|
+
- Maximum size of cached entity
|
26
|
+
- Last-Modified factor: requests that have a Last-Modified header but no Expires
|
27
|
+
header have a TTL assigned based on the last modified age of the response:
|
28
|
+
TTL = (Age * Factor), or, 1h = (10h * 0.1)
|
29
|
+
- I wonder if it would be possible to run in threaded mode but with an
|
30
|
+
option to lock before making requests to the backend. The idea is to be
|
31
|
+
able to serve requests from cache in separate threads. This should
|
32
|
+
probably be implemented as a separate middleware component.
|
33
|
+
- stale-while-revalidate
|
34
|
+
- Serve cached copies when down (see: stale-if-error) - e.g., database
|
35
|
+
connection drops and the cache takes over what it can.
|
36
|
+
- When a cache misses due to Vary, try to validate using the best match. Note
|
37
|
+
that you can't do this with a weak validator, so only strong etags can be
|
38
|
+
used.
|
39
|
+
- Consider implementing ESI (http://www.w3.org/TR/esi-lang). This should
|
40
|
+
probably be implemented as a separate middleware component.
|
@@ -0,0 +1,224 @@
|
|
1
|
+
Configuration Language
|
2
|
+
======================
|
3
|
+
|
4
|
+
__Rack::Cache__ includes a configuration system that can be used to specify
|
5
|
+
fairly sophisticated cache policy on a global or per-request basis.
|
6
|
+
|
7
|
+
- [Synopsis](#synopsis)
|
8
|
+
- [Setting Cache Options](#setopt)
|
9
|
+
- [Cache Option Reference](#options)
|
10
|
+
- [Configuration Machinery - Events and Transitions](#machinery)
|
11
|
+
- [Importing Configuration](#import)
|
12
|
+
- [Default Configuration Machinery](#default)
|
13
|
+
- [Notes](#notes)
|
14
|
+
|
15
|
+
<a id='synopsis'></a>
|
16
|
+
|
17
|
+
Synopsis
|
18
|
+
--------
|
19
|
+
|
20
|
+
use Rack::Cache do
|
21
|
+
# set cache related options
|
22
|
+
set :verbose, true
|
23
|
+
set :metastore, 'memcached://localhost:11211'
|
24
|
+
set :entitystore, 'file:/var/cache/rack/body'
|
25
|
+
|
26
|
+
# override events / transitions
|
27
|
+
on :receive do
|
28
|
+
pass! if request.url =~ %r|/dontcache/|
|
29
|
+
error! 402 if request.referrer =~ /digg.com/
|
30
|
+
end
|
31
|
+
|
32
|
+
on :miss do
|
33
|
+
trace 'missed: %s', request.url
|
34
|
+
end
|
35
|
+
|
36
|
+
# bring in other configuration machinery
|
37
|
+
import 'rack/cache/config/breakers'
|
38
|
+
import 'mycacheconfig'
|
39
|
+
end
|
40
|
+
|
41
|
+
<a id='setopt'></a>
|
42
|
+
|
43
|
+
Setting Cache Options
|
44
|
+
---------------------
|
45
|
+
|
46
|
+
Cache options can be set when the __Rack::Cache__ object is created; or by using
|
47
|
+
the `set` method within a configuration block; or by setting a
|
48
|
+
`rack-cache.<option>` variable in __Rack__'s __Environment__.
|
49
|
+
|
50
|
+
When the __Rack::Cache__ object is instantiated:
|
51
|
+
|
52
|
+
use Rack::Cache,
|
53
|
+
:verbose => true,
|
54
|
+
:metastore => 'memcached://localhost:11211/',
|
55
|
+
:entitystore => 'file:/var/cache/rack'
|
56
|
+
|
57
|
+
Using the `set` method within __Rack::Cache__'s configuration context:
|
58
|
+
|
59
|
+
use Rack::Cache do
|
60
|
+
set :verbose, true
|
61
|
+
set :metastore, 'memcached://localhost:11211/'
|
62
|
+
set :entitystore, 'file:/var/cache/rack'
|
63
|
+
end
|
64
|
+
|
65
|
+
Using __Rack__'s __Environment__:
|
66
|
+
|
67
|
+
env.merge!(
|
68
|
+
'rack-cache.verbose' => true,
|
69
|
+
'rack-cache.metastore' => 'memcached://localhost:11211/',
|
70
|
+
'rack-cache.entitystore' => 'file:/var/cache/rack'
|
71
|
+
)
|
72
|
+
|
73
|
+
<a id='options'></a>
|
74
|
+
|
75
|
+
Cache Option Reference
|
76
|
+
----------------------
|
77
|
+
|
78
|
+
Use the following options to customize __Rack::Cache__:
|
79
|
+
|
80
|
+
### `verbose`
|
81
|
+
|
82
|
+
Boolean specifying whether verbose trace logging is enabled. This option is
|
83
|
+
currently enabled (`true`) by default but is likely to be disabled (`false`) in
|
84
|
+
a future release. All log output is written to the `rack.errors` stream, which
|
85
|
+
is typically set to `STDERR`.
|
86
|
+
|
87
|
+
The `trace`, `info`, `warn`, and `error` methods can be used within the
|
88
|
+
configuration context to write messages to the errors stream.
|
89
|
+
|
90
|
+
### `default_ttl`
|
91
|
+
|
92
|
+
An integer specifying the number of seconds a cached object should be considered
|
93
|
+
"fresh" when no explicit freshness information is provided in a response.
|
94
|
+
Explicit `Cache-Control` or `Expires` response headers always override this
|
95
|
+
value. The `default_ttl` option defaults to `0`, meaning responses without
|
96
|
+
explicit freshness information are considered immediately "stale" and will not
|
97
|
+
be served from cache without validation.
|
98
|
+
|
99
|
+
### `metastore`
|
100
|
+
|
101
|
+
A URI specifying the __MetaStore__ implementation used to store request/response
|
102
|
+
meta information. See the [Rack::Cache Storage Documentation](storage.html)
|
103
|
+
for detailed information on different storage implementations.
|
104
|
+
|
105
|
+
If no metastore is specified, the `heap:/` store is assumed. This implementation
|
106
|
+
has significant draw-backs so explicit configuration is recommended.
|
107
|
+
|
108
|
+
### `entitystore`
|
109
|
+
|
110
|
+
A URI specifying the __EntityStore__ implementation used to store
|
111
|
+
response bodies. See the [Rack::Cache Storage Documentation](storage.html)
|
112
|
+
for detailed information on different storage implementations.
|
113
|
+
|
114
|
+
If no entitystore is specified, the `heap:/` store is assumed. This
|
115
|
+
implementation has significant draw-backs so explicit configuration is
|
116
|
+
recommended.
|
117
|
+
|
118
|
+
<a id='machinery'></a>
|
119
|
+
|
120
|
+
Configuration Machinery - Events and Transitions
|
121
|
+
------------------------------------------------
|
122
|
+
|
123
|
+
The configuration machinery is built around a series of interceptable events and
|
124
|
+
transitions controlled by a simple configuration language. The following diagram
|
125
|
+
shows each state (interceptable event) along with their possible transitions:
|
126
|
+
|
127
|
+
<p class='center'>
|
128
|
+
<img src='events.png' alt='Events and Transitions Diagram' />
|
129
|
+
</p>
|
130
|
+
|
131
|
+
Custom logic can be layered onto the `receive`, `hit`, `miss`, `fetch`, `store`,
|
132
|
+
`deliver`, and `pass` events by passing a block to the `on` method:
|
133
|
+
|
134
|
+
on :fetch do
|
135
|
+
trace 'fetched %p from backend application', request.url
|
136
|
+
end
|
137
|
+
|
138
|
+
Here, the `trace` method writes a message to the `rack.errors` stream when a
|
139
|
+
response is fetched from the backend application. The `request` object is a
|
140
|
+
[__Rack::Cache::Request__](./api/classes/Rack/Cache/Request) that can be
|
141
|
+
inspected (and modified) to determine what action should be taken next.
|
142
|
+
|
143
|
+
Event blocks are capable of performing more interesting operations:
|
144
|
+
|
145
|
+
* Transition to a different event or override default caching logic.
|
146
|
+
* Modify the request, response, cache entry, or Rack environment options.
|
147
|
+
* Set the `metastore` or `entitystore` options to select a different storage
|
148
|
+
mechanism / location dynamically.
|
149
|
+
* Collect statistics or log request/response/cache information.
|
150
|
+
|
151
|
+
When an event is triggered, the blocks associated with the event are executed in
|
152
|
+
reverse/FILO order (i.e., the block declared last runs first) until a
|
153
|
+
_transitioning statement_ is encountered. Transitioning statements are suffixed
|
154
|
+
with a bang character (e.g, `pass!`, `store!`, `error!`) and cause the current
|
155
|
+
event to halt and the machine to transition to the subsequent event; control is
|
156
|
+
not returned to the original event. The [default configuration](#default)
|
157
|
+
includes documentation on available transitions for each event.
|
158
|
+
|
159
|
+
The `next` statement can be used to exit an event block without transitioning
|
160
|
+
to another event. Subsequent event blocks are executed until a transitioning
|
161
|
+
statement is encountered:
|
162
|
+
|
163
|
+
on :fetch do
|
164
|
+
next if response.freshness_information?
|
165
|
+
|
166
|
+
if request.url =~ /\/feed$/
|
167
|
+
trace 'feed will expire in fifteen minutes'
|
168
|
+
response.ttl = 15 * 60
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
<a id='import'></a>
|
173
|
+
|
174
|
+
Importing Configuration
|
175
|
+
-----------------------
|
176
|
+
|
177
|
+
Since caching logic can be layered, it's possible to separate various bits of
|
178
|
+
cache policy into files for organization and reuse.
|
179
|
+
|
180
|
+
use Rack::Cache do
|
181
|
+
import 'rack/cache/config/busters'
|
182
|
+
import 'mycacheconfig'
|
183
|
+
|
184
|
+
# more stuff here
|
185
|
+
end
|
186
|
+
|
187
|
+
The `breakers` and `mycacheconfig` configuration files are normal Ruby source
|
188
|
+
files (i.e., they have a `.rb` extension) situated on the `$LOAD_PATH` - the
|
189
|
+
`import` statement works like Ruby's `require` statement but the contents of the
|
190
|
+
files are evaluated in the context of the configuration machinery, as if
|
191
|
+
specified directly in the configuration block.
|
192
|
+
|
193
|
+
The `rack/cache/config/busters.rb` file makes a good example. It hooks into the
|
194
|
+
`fetch` event and adds an impractically long expiration lifetime to any response
|
195
|
+
that includes a cache busting query string:
|
196
|
+
|
197
|
+
<%= File.read('lib/rack/cache/config/busters.rb').gsub(/^/, ' ') %>
|
198
|
+
|
199
|
+
|
200
|
+
<a id='default'></a>
|
201
|
+
|
202
|
+
Default Configuration Machinery
|
203
|
+
-------------------------------
|
204
|
+
|
205
|
+
The `rack/cache/config/default.rb` file is imported when the __Rack::Cache__
|
206
|
+
object is instantiated and before any custom configuration code is executed.
|
207
|
+
It's useful to understand this configuration because it drives the default
|
208
|
+
transitioning logic.
|
209
|
+
|
210
|
+
<%= File.read('lib/rack/cache/config/default.rb').gsub(/^/, ' ') %>
|
211
|
+
|
212
|
+
<a id='notes'></a>
|
213
|
+
|
214
|
+
Notes
|
215
|
+
-----
|
216
|
+
|
217
|
+
The configuration language was inspired by [Varnish][var]'s
|
218
|
+
[VCL configuration language][vcl].
|
219
|
+
|
220
|
+
[var]: http://varnish.projects.linpro.no/
|
221
|
+
"Varnish HTTP accelerator"
|
222
|
+
|
223
|
+
[vcl]: http://tomayko.com/man/vcl
|
224
|
+
"VCL(7) -- Varnish Configuration Language Manual Page"
|