rack-cache 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
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"
|