josh-rack-cache 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +167 -0
- data/COPYING +18 -0
- data/README +110 -0
- data/Rakefile +137 -0
- data/TODO +27 -0
- data/doc/configuration.markdown +112 -0
- data/doc/faq.markdown +141 -0
- data/doc/index.markdown +121 -0
- data/doc/layout.html.erb +34 -0
- data/doc/license.markdown +24 -0
- data/doc/rack-cache.css +362 -0
- data/doc/server.ru +34 -0
- data/doc/storage.markdown +164 -0
- data/example/sinatra/app.rb +25 -0
- data/example/sinatra/views/index.erb +44 -0
- data/lib/rack/cache.rb +45 -0
- data/lib/rack/cache/appengine.rb +52 -0
- data/lib/rack/cache/cachecontrol.rb +193 -0
- data/lib/rack/cache/context.rb +253 -0
- data/lib/rack/cache/entitystore.rb +339 -0
- data/lib/rack/cache/key.rb +52 -0
- data/lib/rack/cache/metastore.rb +407 -0
- data/lib/rack/cache/options.rb +150 -0
- data/lib/rack/cache/request.rb +33 -0
- data/lib/rack/cache/response.rb +267 -0
- data/lib/rack/cache/storage.rb +62 -0
- data/rack-cache.gemspec +70 -0
- data/test/cache_test.rb +38 -0
- data/test/cachecontrol_test.rb +139 -0
- data/test/context_test.rb +774 -0
- data/test/entitystore_test.rb +230 -0
- data/test/key_test.rb +50 -0
- data/test/metastore_test.rb +302 -0
- data/test/options_test.rb +77 -0
- data/test/pony.jpg +0 -0
- data/test/request_test.rb +19 -0
- data/test/response_test.rb +178 -0
- data/test/spec_setup.rb +237 -0
- data/test/storage_test.rb +94 -0
- metadata +118 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
Configuration
|
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
|
+
<a id='setopt'></a>
|
8
|
+
|
9
|
+
Setting Cache Options
|
10
|
+
---------------------
|
11
|
+
|
12
|
+
Cache options can be set when the __Rack::Cache__ object is created,
|
13
|
+
or by setting a `rack-cache.<option>` variable in __Rack__'s
|
14
|
+
__Environment__.
|
15
|
+
|
16
|
+
When the __Rack::Cache__ object is instantiated:
|
17
|
+
|
18
|
+
use Rack::Cache,
|
19
|
+
:verbose => true,
|
20
|
+
:metastore => 'memcached://localhost:11211/',
|
21
|
+
:entitystore => 'file:/var/cache/rack'
|
22
|
+
|
23
|
+
Using __Rack__'s __Environment__:
|
24
|
+
|
25
|
+
env.merge!(
|
26
|
+
'rack-cache.verbose' => true,
|
27
|
+
'rack-cache.metastore' => 'memcached://localhost:11211/',
|
28
|
+
'rack-cache.entitystore' => 'file:/var/cache/rack'
|
29
|
+
)
|
30
|
+
|
31
|
+
<a id='options'></a>
|
32
|
+
|
33
|
+
Cache Option Reference
|
34
|
+
----------------------
|
35
|
+
|
36
|
+
Use the following options to customize __Rack::Cache__:
|
37
|
+
|
38
|
+
### `verbose`
|
39
|
+
|
40
|
+
Boolean specifying whether verbose trace logging is enabled. This option is
|
41
|
+
currently enabled (`true`) by default but is likely to be disabled (`false`) in
|
42
|
+
a future release. All log output is written to the `rack.errors` stream, which
|
43
|
+
is typically set to `STDERR`.
|
44
|
+
|
45
|
+
The `trace`, `info`, `warn`, and `error` methods can be used within the
|
46
|
+
configuration context to write messages to the errors stream.
|
47
|
+
|
48
|
+
### `default_ttl`
|
49
|
+
|
50
|
+
An integer specifying the number of seconds a cached object should be considered
|
51
|
+
"fresh" when no explicit freshness information is provided in a response.
|
52
|
+
Explicit `Cache-Control` or `Expires` response headers always override this
|
53
|
+
value. The `default_ttl` option defaults to `0`, meaning responses without
|
54
|
+
explicit freshness information are considered immediately "stale" and will not
|
55
|
+
be served from cache without validation.
|
56
|
+
|
57
|
+
### `metastore`
|
58
|
+
|
59
|
+
A URI specifying the __MetaStore__ implementation used to store request/response
|
60
|
+
meta information. See the [Rack::Cache Storage Documentation](storage.html)
|
61
|
+
for detailed information on different storage implementations.
|
62
|
+
|
63
|
+
If no metastore is specified, the `heap:/` store is assumed. This implementation
|
64
|
+
has significant draw-backs so explicit configuration is recommended.
|
65
|
+
|
66
|
+
### `entitystore`
|
67
|
+
|
68
|
+
A URI specifying the __EntityStore__ implementation used to store
|
69
|
+
response bodies. See the [Rack::Cache Storage Documentation](storage.html)
|
70
|
+
for detailed information on different storage implementations.
|
71
|
+
|
72
|
+
If no entitystore is specified, the `heap:/` store is assumed. This
|
73
|
+
implementation has significant draw-backs so explicit configuration is
|
74
|
+
recommended.
|
75
|
+
|
76
|
+
### `private_headers`
|
77
|
+
|
78
|
+
An array of request header names that cause the response to be treated with
|
79
|
+
private cache control semantics. The default value is `['Authorization', 'Cookie']`.
|
80
|
+
If any of these headers are present in the request, the response is considered
|
81
|
+
private and will not be cached _unless_ the response is explicitly marked public
|
82
|
+
(e.g., `Cache-Control: public`).
|
83
|
+
|
84
|
+
### `allow_reload`
|
85
|
+
|
86
|
+
A boolean specifying whether reload requests sent by the client should be
|
87
|
+
honored by the cache. When this option is enabled (`rack-cache.allow_reload`
|
88
|
+
is `true`), requests that include a `Cache-Control: no-cache` header cause
|
89
|
+
the cache to discard anything it has stored for the request and ask that the
|
90
|
+
response be fully generated.
|
91
|
+
|
92
|
+
Most browsers include a `Cache-Control: no-cache` header when the user performs
|
93
|
+
a "hard refresh" (e.g., holding `Shift` while clicking the "Refresh" button).
|
94
|
+
|
95
|
+
*IMPORTANT: Enabling this option globally allows all clients to break your cache.*
|
96
|
+
|
97
|
+
### `allow_revalidate`
|
98
|
+
|
99
|
+
A boolean specifying whether revalidate requests sent by the client should be
|
100
|
+
honored by the cache. When this option is enabled (`rack-cache.allow_revalidate`
|
101
|
+
is `true`), requests that include a `Cache-Control: max-age=0` header cause the
|
102
|
+
cache to assume its copy of the response is stale, resulting in a conditional
|
103
|
+
GET / validation request to be sent to the server.
|
104
|
+
|
105
|
+
Most browsers include a `Cache-Control: max-age=0` header when the user performs
|
106
|
+
a refresh (e.g., clicking the "Refresh" button).
|
107
|
+
|
108
|
+
*IMPORTANT: Enabling this option globally allows all clients to break your cache.*
|
109
|
+
|
110
|
+
### `cache_key`
|
111
|
+
|
112
|
+
TODO: Document custom cache keys
|
data/doc/faq.markdown
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
Frequently Asked Questions
|
2
|
+
==========================
|
3
|
+
|
4
|
+
<p class='intro'>
|
5
|
+
<strong>NOTE:</strong> This is a work in progress. Please send questions, comments, or
|
6
|
+
suggestions to <a href="mailto:r@tomayko.com">r@tomayko.com</a>.
|
7
|
+
</p>
|
8
|
+
|
9
|
+
General
|
10
|
+
-------
|
11
|
+
|
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
|
+
|
21
|
+
<a class='hash' id='why-not-squid' href='#why-not-squid'>#</a>
|
22
|
+
|
23
|
+
### Q: Why Rack::Cache? Why not Squid, Varnish, Perlbol, etc.?
|
24
|
+
|
25
|
+
__Rack::Cache__ is often easier to setup as part of your existing Ruby
|
26
|
+
application than a separate caching system. __Rack::Cache__ runs entirely inside
|
27
|
+
your backend application processes - no separate / external process is required.
|
28
|
+
This lets __Rack::Cache__ scale down to development environments and simple
|
29
|
+
deployments very easily while not sacrificing the benefits of a standards-based
|
30
|
+
approach to caching.
|
31
|
+
|
32
|
+
|
33
|
+
<a class='hash' id='why-not-rails' href='#why-not-rails'>#</a>
|
34
|
+
|
35
|
+
### Q: Why Rack::Cache? Why not use Rails/Merb/FrameworkX's caching system?
|
36
|
+
|
37
|
+
__Rack::Cache__ takes a standards-based approach to caching that provides some
|
38
|
+
benefits over framework-integrated systems. It uses standard HTTP headers
|
39
|
+
(`Expires`, `Cache-Control`, `Etag`, `Last-Modified`, etc.) to determine
|
40
|
+
what/when to cache. Designing applications to support these standard HTTP
|
41
|
+
mechanisms gives the benefit of being able to switch to a different HTTP
|
42
|
+
cache implementation in the future.
|
43
|
+
|
44
|
+
In addition, using a standards-based approach to caching creates a clear
|
45
|
+
separation between application and caching logic. The application need only
|
46
|
+
specify a basic set of information about the response and all decisions
|
47
|
+
regarding how and when to cache is moved into the caching layer.
|
48
|
+
|
49
|
+
|
50
|
+
<a class='hash' id='scale' href='#scale'>#</a>
|
51
|
+
|
52
|
+
### Q: Will Rack::Cache make my app scale?
|
53
|
+
|
54
|
+
No. Your design is the only thing that can make your app scale.
|
55
|
+
|
56
|
+
Also, __Rack::Cache__ is not overly optimized for performance. The main goal of
|
57
|
+
the project is to provide a portable, easy-to-configure, and standards-based
|
58
|
+
caching solution for small to medium sized deployments. More sophisticated /
|
59
|
+
performant caching systems (e.g., [Varnish][v], [Squid][s],
|
60
|
+
[httpd/mod-cache][h]) may be more appropriate for large deployments with
|
61
|
+
crazy-land throughput requirements.
|
62
|
+
|
63
|
+
[v]: http://varnish.projects.linpro.no/
|
64
|
+
[s]: http://www.squid-cache.org/
|
65
|
+
[h]: http://httpd.apache.org/docs/2.0/mod/mod_cache.html
|
66
|
+
|
67
|
+
|
68
|
+
Features
|
69
|
+
--------
|
70
|
+
|
71
|
+
|
72
|
+
<a class='hash' id='validation' href='#validation'>#</a>
|
73
|
+
|
74
|
+
### Q: Does Rack::Cache support validation?
|
75
|
+
|
76
|
+
Yes. Both freshness and validation-based caching is supported. A response
|
77
|
+
will be cached if it has a freshness lifetime (e.g., `Expires` or
|
78
|
+
`Cache-Control: max-age=N` headers) and/or includes a validator (e.g.,
|
79
|
+
`Last-Modified` or `ETag` headers). When the cache hits and the response is
|
80
|
+
fresh, it's delivered immediately without talking to the backend application;
|
81
|
+
when the cache is stale, the cached response is validated using a conditional
|
82
|
+
GET request.
|
83
|
+
|
84
|
+
|
85
|
+
<a class='hash' id='fragments' href='#fragments'>#</a>
|
86
|
+
|
87
|
+
### Q: Does Rack::Cache support fragment caching?
|
88
|
+
|
89
|
+
Not really. __Rack::Cache__ deals with entire responses and doesn't know
|
90
|
+
anything about how your application constructs them.
|
91
|
+
|
92
|
+
However, something like [ESI](http://www.w3.org/TR/esi-lang) may be implemented
|
93
|
+
in the future (likely as a separate Rack middleware component that could be
|
94
|
+
situated upstream from Rack::Cache), which would allow applications to compose
|
95
|
+
responses based on several "fragment resources". Each fragment would have its
|
96
|
+
own cache policy.
|
97
|
+
|
98
|
+
|
99
|
+
<a class='hash' id='manual-purge' href='#manual-purge'>#</a>
|
100
|
+
|
101
|
+
### Q: How do I manually purge or expire a cached entry?
|
102
|
+
|
103
|
+
Although planned, there is currently no mechanism for manually purging
|
104
|
+
an entry stored in the cache.
|
105
|
+
|
106
|
+
Note that using an `Expires` or `Cache-Control: max-age=N` header and relying on
|
107
|
+
manual purge to invalidate cached entry can often be implemented more simply
|
108
|
+
using efficient validation based caching (`Last-Modified`, `Etag`). Many web
|
109
|
+
frameworks are based entirely on manual purge and do not support validation at
|
110
|
+
the cache level.
|
111
|
+
|
112
|
+
|
113
|
+
<a class='hash' id='efficient-validation' href='#efficient-validation'>#</a>
|
114
|
+
|
115
|
+
### Q: What does "Efficient Validation" mean?
|
116
|
+
|
117
|
+
It means that your application performs only the processing necessary to
|
118
|
+
determine if a response is valid before sending a `304 Not Modified` in response
|
119
|
+
to a conditional GET request. Many applications that perform validation do so
|
120
|
+
only after the entire response has been generated, which provides bandwidth
|
121
|
+
savings but results in no CPU/IO savings. Implementing validation efficiently
|
122
|
+
can increase backend application throughput significantly when fronted by a
|
123
|
+
validating caching system (like __Rack::Cache__).
|
124
|
+
|
125
|
+
[Here's an example Rack application](http://gist.github.com/9395) that performs
|
126
|
+
efficient validation.
|
127
|
+
|
128
|
+
|
129
|
+
<a class='hash' id='orly' href='#orly'>#</a>
|
130
|
+
|
131
|
+
### Q: Did you just make that up?
|
132
|
+
|
133
|
+
Yes.
|
134
|
+
|
135
|
+
|
136
|
+
<a class='hash' id='https' href='#https'>#</a>
|
137
|
+
|
138
|
+
### Q: Can I do HTTPS with Rack::Cache?
|
139
|
+
|
140
|
+
Sure. HTTPS is typically managed by a front-end web server so this isn't really
|
141
|
+
relevant to Rack::Cache.
|
data/doc/index.markdown
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
__Rack::Cache__ is suitable as a quick drop-in component to enable HTTP caching
|
2
|
+
for [Rack][]-based applications that produce freshness (`Expires`,
|
3
|
+
`Cache-Control`) and/or validation (`Last-Modified`, `ETag`) information.
|
4
|
+
|
5
|
+
* Standards-based (see [RFC 2616][rfc] / [Section 13][s13]).
|
6
|
+
* Freshness/expiration based caching
|
7
|
+
* Validation
|
8
|
+
* Vary support
|
9
|
+
* Portable: 100% Ruby / works with any [Rack][]-enabled framework.
|
10
|
+
* Disk, memcached, and heap memory [storage backends][storage].
|
11
|
+
|
12
|
+
News
|
13
|
+
----
|
14
|
+
|
15
|
+
* Rack::Cache 0.5 was released on May 25, 2009. See the
|
16
|
+
[`CHANGES`](http://github.com/rtomayko/rack-cache/blob/0.5.0/CHANGES) file
|
17
|
+
for details.
|
18
|
+
* [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.
|
19
|
+
* [RailsLab's Advanced HTTP Caching Screencast](http://railslab.newrelic.com/2009/02/26/episode-11-advanced-http-caching)
|
20
|
+
is a really great review of HTTP caching concepts and shows how to
|
21
|
+
use Rack::Cache with Rails.
|
22
|
+
|
23
|
+
Installation
|
24
|
+
------------
|
25
|
+
|
26
|
+
$ sudo gem install rack-cache
|
27
|
+
|
28
|
+
Or, from a local working copy:
|
29
|
+
|
30
|
+
$ git clone git://github.com/rtomayko/rack-cache.git
|
31
|
+
$ rake package && sudo rake install
|
32
|
+
|
33
|
+
Basic Usage
|
34
|
+
-----------
|
35
|
+
|
36
|
+
__Rack::Cache__ is implemented as a piece of [Rack][] middleware and can be used
|
37
|
+
with any __Rack__-based application. If your application includes a rackup
|
38
|
+
(`.ru`) file or uses __Rack::Builder__ to construct the application pipeline,
|
39
|
+
simply `require` and `use` as follows:
|
40
|
+
|
41
|
+
require 'rack/cache'
|
42
|
+
|
43
|
+
use Rack::Cache,
|
44
|
+
:verbose => true,
|
45
|
+
:metastore => 'file:/var/cache/rack/meta'
|
46
|
+
:entitystore => 'file:/var/cache/rack/body'
|
47
|
+
|
48
|
+
run app
|
49
|
+
|
50
|
+
Assuming you've designed your backend application to take advantage of HTTP's
|
51
|
+
caching features, no further code or configuration is required for basic
|
52
|
+
caching.
|
53
|
+
|
54
|
+
More
|
55
|
+
----
|
56
|
+
|
57
|
+
* [Configuration Options][config] - how to set cache options.
|
58
|
+
|
59
|
+
* [Cache Storage Documentation][storage] - detailed information on the various
|
60
|
+
storage implementations available in __Rack::Cache__ and how to choose the one
|
61
|
+
that's best for your application.
|
62
|
+
|
63
|
+
* [Things Caches Do][things] - an illustrated guide to how HTTP gateway
|
64
|
+
caches work with pointers to other useful resources on HTTP caching.
|
65
|
+
|
66
|
+
* [GitHub Repository](http://github.com/rtomayko/rack-cache/) - get your
|
67
|
+
fork on.
|
68
|
+
|
69
|
+
* [Mailing List](http://groups.google.com/group/rack-cache) - for hackers
|
70
|
+
and users (`rack-cache@groups.google.com`).
|
71
|
+
|
72
|
+
* [FAQ](./faq) - Frequently Asked Questions about __Rack::Cache__.
|
73
|
+
|
74
|
+
* [RDoc API Documentation](./api/) - Mostly worthless if you just want to use
|
75
|
+
__Rack::Cache__ in your application but mildly insightful if you'd like to
|
76
|
+
get a feel for how the system has been put together; I recommend
|
77
|
+
[reading the source](http://github.com/rtomayko/rack-cache/master/lib/rack/cache).
|
78
|
+
|
79
|
+
|
80
|
+
See Also
|
81
|
+
--------
|
82
|
+
|
83
|
+
The overall design of __Rack::Cache__ is based largely on the work of the
|
84
|
+
internet standards community. The following resources provide a good starting
|
85
|
+
point for exploring the basic concepts of HTTP caching:
|
86
|
+
|
87
|
+
* Mark Nottingham's [Caching Tutorial](http://www.mnot.net/cache_docs/),
|
88
|
+
especially the short section on
|
89
|
+
[How Web Caches Work](http://www.mnot.net/cache_docs/#WORK)
|
90
|
+
|
91
|
+
* Joe Gregorio's [Doing HTTP Caching Right](http://www.xml.com/lpt/a/1642)
|
92
|
+
|
93
|
+
* [RFC 2616](http://www.ietf.org/rfc/rfc2616.txt), especially
|
94
|
+
[Section 13, "Caching in HTTP"](http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html)
|
95
|
+
|
96
|
+
__Rack::Cache__ takes (_liberally_) various concepts from
|
97
|
+
[Varnish](http://varnish.projects.linpro.no/) and
|
98
|
+
[Django's cache framework](http://docs.djangoproject.com/en/dev/topics/cache/).
|
99
|
+
|
100
|
+
License
|
101
|
+
-------
|
102
|
+
|
103
|
+
__Rack::Cache__ is Copyright © 2008
|
104
|
+
by [Ryan Tomayko](http://tomayko.com/about)
|
105
|
+
and is provided under [the MIT license](./license)
|
106
|
+
|
107
|
+
[config]: ./configuration "Rack::Cache Configuration Language Documentation"
|
108
|
+
[storage]: ./storage "Rack::Cache Storage Documentation"
|
109
|
+
[things]: http://tomayko.com/writings/things-caches-do
|
110
|
+
|
111
|
+
[rfc]: http://tools.ietf.org/html/rfc2616
|
112
|
+
"RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1 [ietf.org]"
|
113
|
+
|
114
|
+
[s13]: http://tools.ietf.org/html/rfc2616#section-13
|
115
|
+
"RFC 2616 / Section 13 Caching in HTTP"
|
116
|
+
|
117
|
+
[rack]: http://rack.rubyforge.org/
|
118
|
+
"Rack: a Ruby Webserver Interface"
|
119
|
+
|
120
|
+
[vcl]: http://tomayko.com/man/vcl
|
121
|
+
"VCL(7) -- Varnish Configuration Language Manual Page"
|
data/doc/layout.html.erb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang='en'>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>
|
5
|
+
<title>Rack::Cache <%= title %></title>
|
6
|
+
<link rel='stylesheet' href='rack-cache.css' type='text/css' media='all'>
|
7
|
+
<script type='text/javascript' src='http://code.jquery.com/jquery-1.2.3.js'></script>
|
8
|
+
<script type='text/javascript' src='http://tomayko.com/js/tomayko.js'></script>
|
9
|
+
</head>
|
10
|
+
<body>
|
11
|
+
<div id='container'>
|
12
|
+
<div id='header'>
|
13
|
+
<h1><a href="./">rack-cache</a></h1>
|
14
|
+
<p>
|
15
|
+
<a href="./configuration" title='Configuration Language Documentation'>Config</a> |
|
16
|
+
<a href="./storage" title='Cache Storage Documentation'>Storage</a> |
|
17
|
+
<a href="http://tomayko.com/writings/things-caches-do" title="Things Caches Do">Things</a> |
|
18
|
+
<a href="./faq" title='Frequently Asked Questions'>FAQ</a> |
|
19
|
+
<a href="./api/" title='Fucking Sucks.'>RDoc</a>
|
20
|
+
</p>
|
21
|
+
</div>
|
22
|
+
<div id='content'><%= content %></div>
|
23
|
+
<div id='footer'>
|
24
|
+
<p class='rights'>
|
25
|
+
Copyright
|
26
|
+
<a href="./license" rel="license">©</a>
|
27
|
+
2003-2008
|
28
|
+
by
|
29
|
+
<a href='http://tomayko.com/about' rel='me author'>Ryan Tomayko</a>
|
30
|
+
</p>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
</body>
|
34
|
+
</html>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
License (MIT)
|
2
|
+
=============
|
3
|
+
|
4
|
+
__Rack::Cache__ is Copyright © 2008
|
5
|
+
by [Ryan Tomayko](http://tomayko.com/about)
|
6
|
+
|
7
|
+
<pre class='license'>
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
of this software and associated documentation files (the "Software"), to
|
10
|
+
deal in the Software without restriction, including without limitation the
|
11
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
12
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
13
|
+
furnished to do so, subject to the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be included in
|
16
|
+
all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
21
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
22
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
23
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
</pre>
|
data/doc/rack-cache.css
ADDED
@@ -0,0 +1,362 @@
|
|
1
|
+
/* rack-cache.css
|
2
|
+
*---------------------------------------------------------------------------
|
3
|
+
* Copyright (C) 2005-08 Ryan Tomayko <r@tomayko.com>
|
4
|
+
*/
|
5
|
+
|
6
|
+
|
7
|
+
/* 18px base font size / 25px baseline */
|
8
|
+
body {
|
9
|
+
font-size:112.5%; /* 18px (probably) */
|
10
|
+
line-height:1.3888; /* 25px */
|
11
|
+
letter-spacing:-0.02em;
|
12
|
+
margin:0 10px;
|
13
|
+
font-family: 'lucida sans unicode', 'lucida grande',
|
14
|
+
helvetica, 'bitstream vera sans', sans-serif;
|
15
|
+
color:#556;
|
16
|
+
background-color:#fff;
|
17
|
+
}
|
18
|
+
|
19
|
+
#container {
|
20
|
+
max-width:45em;
|
21
|
+
margin:0 auto;
|
22
|
+
}
|
23
|
+
|
24
|
+
h1, h2, h3 {
|
25
|
+
font-family:georgia, 'bitstream vera sans serif', 'lucida grande',
|
26
|
+
helvetica, verdana, sans-serif;
|
27
|
+
font-weight:normal;
|
28
|
+
letter-spacing:-0.05em;
|
29
|
+
color:#000;
|
30
|
+
}
|
31
|
+
i, em {
|
32
|
+
font-style:italic;
|
33
|
+
}
|
34
|
+
b, strong {
|
35
|
+
font-weight:normal;
|
36
|
+
color:#000;
|
37
|
+
}
|
38
|
+
blockquote {
|
39
|
+
color:#555;
|
40
|
+
}
|
41
|
+
blockquote em {
|
42
|
+
color:#333;
|
43
|
+
font-style:italic;
|
44
|
+
}
|
45
|
+
blockquote strong {
|
46
|
+
color:#333;
|
47
|
+
font-weight: normal;
|
48
|
+
}
|
49
|
+
dt {
|
50
|
+
font-weight:bold;
|
51
|
+
color:#000;
|
52
|
+
}
|
53
|
+
tt, pre, code, samp, kbd {
|
54
|
+
font-family: consolas, 'lucida console', 'bitstream vera sans mono',
|
55
|
+
'courier new', monospace;
|
56
|
+
color: #000;
|
57
|
+
}
|
58
|
+
pre {
|
59
|
+
color:#333;
|
60
|
+
background-color:#f9f9f9;
|
61
|
+
}
|
62
|
+
code {
|
63
|
+
color:#007A00;
|
64
|
+
}
|
65
|
+
pre code {
|
66
|
+
color:#333;
|
67
|
+
}
|
68
|
+
pre.license {
|
69
|
+
border:0;
|
70
|
+
background:#fff;
|
71
|
+
padding:0;
|
72
|
+
font-size:1.1em;
|
73
|
+
}
|
74
|
+
a, a:link {
|
75
|
+
color:#023;
|
76
|
+
background:#eef;
|
77
|
+
}
|
78
|
+
a:visited {
|
79
|
+
color:#345;
|
80
|
+
background:#fff;
|
81
|
+
}
|
82
|
+
a:hover {
|
83
|
+
background:#ccf;
|
84
|
+
color:#000;
|
85
|
+
text-decoration:none;
|
86
|
+
}
|
87
|
+
|
88
|
+
|
89
|
+
/* TYPOGRAPHY */
|
90
|
+
|
91
|
+
p, ul, ol, dl, pre, blockquote, table, form {
|
92
|
+
margin:1em 0;
|
93
|
+
}
|
94
|
+
dl {
|
95
|
+
margin-left:2em;
|
96
|
+
}
|
97
|
+
hr {
|
98
|
+
color:#eee;
|
99
|
+
background-color:#ccc;
|
100
|
+
border:0;
|
101
|
+
height:1px;
|
102
|
+
margin:1.5em 0;
|
103
|
+
}
|
104
|
+
blockquote {
|
105
|
+
font-size:0.83333em; /* 15px */
|
106
|
+
line-height:1.66666; /* 25px */
|
107
|
+
margin:1.2em 3em;
|
108
|
+
padding:0;
|
109
|
+
}
|
110
|
+
tt, pre, code, samp, kbd {
|
111
|
+
font-size: 16px;
|
112
|
+
line-height:1.1;
|
113
|
+
}
|
114
|
+
pre {
|
115
|
+
margin:1.5em 0;
|
116
|
+
padding:6px 4px 4px 6px;
|
117
|
+
border:1px solid #eee;
|
118
|
+
border-left-width:20px;
|
119
|
+
overflow:auto;
|
120
|
+
}
|
121
|
+
h1 {
|
122
|
+
font-size:2.3333em; /* 42px */
|
123
|
+
line-height:1.1904; /* 50px */
|
124
|
+
margin:0.5952em 0; /* 25px */
|
125
|
+
}
|
126
|
+
h2 {
|
127
|
+
font-size:1.66666667em; /* 30px */
|
128
|
+
line-height:1.2; /* 36px */
|
129
|
+
margin:1em 0;
|
130
|
+
}
|
131
|
+
h3 {
|
132
|
+
font-size:1.33333333em; /* 22px */
|
133
|
+
line-height:1.13636363; /* 25px */
|
134
|
+
margin:1em 0;
|
135
|
+
}
|
136
|
+
h3 code{
|
137
|
+
font-size:0.95em;
|
138
|
+
color:#000;
|
139
|
+
}
|
140
|
+
h4 {
|
141
|
+
font-size:1em;
|
142
|
+
font-weight:bold;
|
143
|
+
line-height:1.5;
|
144
|
+
margin:1em 0;
|
145
|
+
}
|
146
|
+
p small {
|
147
|
+
font-size:0.8333; /* 15px */
|
148
|
+
line-height:1.2;
|
149
|
+
}
|
150
|
+
|
151
|
+
/* Tables
|
152
|
+
--------------------------------------------------------------------------- */
|
153
|
+
|
154
|
+
table {
|
155
|
+
width:100%;
|
156
|
+
border-style:none;
|
157
|
+
border-color:#ddd;
|
158
|
+
padding:0;
|
159
|
+
}
|
160
|
+
|
161
|
+
th, td {
|
162
|
+
padding: 4px 10px 4px 5px;
|
163
|
+
border-style:solid;
|
164
|
+
border-color:#fff;
|
165
|
+
}
|
166
|
+
|
167
|
+
th {
|
168
|
+
font-weight: bold;
|
169
|
+
background: #eef;
|
170
|
+
}
|
171
|
+
|
172
|
+
td {
|
173
|
+
background: #f9f9f9;
|
174
|
+
}
|
175
|
+
|
176
|
+
tfoot {
|
177
|
+
font-style: italic;
|
178
|
+
}
|
179
|
+
|
180
|
+
caption {
|
181
|
+
background: #eee;
|
182
|
+
}
|
183
|
+
|
184
|
+
/* Header / Titling
|
185
|
+
--------------------------------------------------------------------------- */
|
186
|
+
|
187
|
+
#header {
|
188
|
+
text-align:left;
|
189
|
+
margin:1.5em auto 2em;
|
190
|
+
float:left;
|
191
|
+
width:100%;
|
192
|
+
padding-bottom:1.5em;
|
193
|
+
border-bottom:1px solid #777;
|
194
|
+
}
|
195
|
+
#header h1 {
|
196
|
+
font-family: 'lucida sans unicode', 'lucida grande',
|
197
|
+
helvetica, 'bitstream vera sans', sans-serif;
|
198
|
+
font-size:5em;
|
199
|
+
font-weight:bold;
|
200
|
+
line-height:1;
|
201
|
+
margin:0;
|
202
|
+
float:left;
|
203
|
+
color:#000;
|
204
|
+
letter-spacing:-0.08em;
|
205
|
+
}
|
206
|
+
#header h1 a, #header h1 a:link, #header h1 a:visited, #header h1 a:hover {
|
207
|
+
color:#000;
|
208
|
+
text-decoration:none;
|
209
|
+
background:transparent;
|
210
|
+
}
|
211
|
+
#header p {
|
212
|
+
margin: 0;
|
213
|
+
line-height:1.8;
|
214
|
+
color: #777;
|
215
|
+
text-transform:capitalize;
|
216
|
+
font-variant:small-caps;
|
217
|
+
float:right;
|
218
|
+
}
|
219
|
+
#header a, #header a:link, #header a:visited {
|
220
|
+
color:#445;
|
221
|
+
background:#fff;
|
222
|
+
}
|
223
|
+
#header a:hover {
|
224
|
+
background:#ccf;
|
225
|
+
color:#000;
|
226
|
+
text-decoration:none;
|
227
|
+
}
|
228
|
+
#content {
|
229
|
+
clear:both;
|
230
|
+
}
|
231
|
+
|
232
|
+
/* FOOTER */
|
233
|
+
|
234
|
+
#footer {
|
235
|
+
clear:both;
|
236
|
+
color:#555;
|
237
|
+
font-size:0.88888888em;
|
238
|
+
line-height:1.5625;
|
239
|
+
border-top:1px solid #ddd;
|
240
|
+
padding:19px 0 0 0;
|
241
|
+
margin:40px 0 20px 0;
|
242
|
+
text-align:center;
|
243
|
+
}
|
244
|
+
#footer p {
|
245
|
+
margin:0;
|
246
|
+
}
|
247
|
+
#footer form {
|
248
|
+
float:right;
|
249
|
+
}
|
250
|
+
#footer input{
|
251
|
+
font-size:10px;
|
252
|
+
}
|
253
|
+
|
254
|
+
/* MISC HELPER STYLES */
|
255
|
+
|
256
|
+
ul.clean, ol.clean {
|
257
|
+
list-style-type: none;
|
258
|
+
padding-left: 0;
|
259
|
+
}
|
260
|
+
.caps {
|
261
|
+
font-variant:small-caps;
|
262
|
+
}
|
263
|
+
.clear {
|
264
|
+
clear:both;
|
265
|
+
}
|
266
|
+
.left{
|
267
|
+
float:left;
|
268
|
+
}
|
269
|
+
.right{
|
270
|
+
float:right;
|
271
|
+
}
|
272
|
+
.center{
|
273
|
+
text-align:center;
|
274
|
+
}
|
275
|
+
.intro {
|
276
|
+
font-size:0.833333em; /* 15px */
|
277
|
+
line-height:1.666667; /* 25px */
|
278
|
+
border:1px solid #ccc;
|
279
|
+
padding:0.5em;
|
280
|
+
font-style:italic;
|
281
|
+
color:#555;
|
282
|
+
}
|
283
|
+
a.hash,
|
284
|
+
a.hash:link,
|
285
|
+
a.hash:visited {
|
286
|
+
display:block;
|
287
|
+
float:right;
|
288
|
+
background:#fff;
|
289
|
+
font-size:0.8em;
|
290
|
+
text-decoration:none;
|
291
|
+
line-height:2;
|
292
|
+
color:#999;
|
293
|
+
}
|
294
|
+
a.hash:hover {
|
295
|
+
color:MediumOrchid;
|
296
|
+
}
|
297
|
+
|
298
|
+
/* PRINT */
|
299
|
+
|
300
|
+
@media print {
|
301
|
+
html, body, #container {
|
302
|
+
margin:0;
|
303
|
+
}
|
304
|
+
#container {
|
305
|
+
width:100%;
|
306
|
+
max-width:100%;
|
307
|
+
}
|
308
|
+
#header {
|
309
|
+
margin-top:0;
|
310
|
+
}
|
311
|
+
#header p {
|
312
|
+
display:none;
|
313
|
+
}
|
314
|
+
#footer {
|
315
|
+
display:none;
|
316
|
+
}
|
317
|
+
a, a:link, a:visited {
|
318
|
+
color:#000;
|
319
|
+
background:#fff;
|
320
|
+
text-decoration:none;
|
321
|
+
}
|
322
|
+
pre.license {
|
323
|
+
font-size:0.95em;
|
324
|
+
}
|
325
|
+
@page {
|
326
|
+
size:8.5in 11in;
|
327
|
+
}
|
328
|
+
}
|
329
|
+
|
330
|
+
/* PRETTIFICATION OF SOURCE CODE */
|
331
|
+
|
332
|
+
.str { color: #181; font-style:italic; }
|
333
|
+
.kwd { color: #369; }
|
334
|
+
.com { color: #666; }
|
335
|
+
.typ { color: #c40; }
|
336
|
+
.lit { color: #900; }
|
337
|
+
.pun { color: #000; font-weight: bold; }
|
338
|
+
.pln { color: #333; }
|
339
|
+
.tag { color: #369; font-weight: bold; }
|
340
|
+
.atn { color: #939; font-weight: bold }
|
341
|
+
.atv { color: #181; }
|
342
|
+
.dec { color: #606; }
|
343
|
+
|
344
|
+
@media print {
|
345
|
+
.str { color: #060; }
|
346
|
+
.kwd { color: #006; font-weight: bold; }
|
347
|
+
.com { color: #600; font-style: italic; }
|
348
|
+
.typ { color: #404; font-weight: bold; }
|
349
|
+
.lit { color: #044; }
|
350
|
+
.pun { color: #440; }
|
351
|
+
.pln { color: #000; }
|
352
|
+
.tag { color: #006; font-weight: bold; }
|
353
|
+
.atn { color: #404; }
|
354
|
+
.atv { color: #060; }
|
355
|
+
}
|
356
|
+
|
357
|
+
/* FUCKING IE */
|
358
|
+
|
359
|
+
* html body{width:40em}
|
360
|
+
* html div.index{width:34.5em}
|
361
|
+
|
362
|
+
/* vim: set ft=css ts=4 sw=4 noexpandtab: */
|