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.

Files changed (44) hide show
  1. data/CHANGES +27 -0
  2. data/COPYING +18 -0
  3. data/README +96 -0
  4. data/Rakefile +144 -0
  5. data/TODO +40 -0
  6. data/doc/configuration.markdown +224 -0
  7. data/doc/events.dot +27 -0
  8. data/doc/faq.markdown +133 -0
  9. data/doc/index.markdown +113 -0
  10. data/doc/layout.html.erb +33 -0
  11. data/doc/license.markdown +24 -0
  12. data/doc/rack-cache.css +362 -0
  13. data/doc/storage.markdown +162 -0
  14. data/lib/rack/cache.rb +51 -0
  15. data/lib/rack/cache/config.rb +65 -0
  16. data/lib/rack/cache/config/busters.rb +16 -0
  17. data/lib/rack/cache/config/default.rb +134 -0
  18. data/lib/rack/cache/config/no-cache.rb +13 -0
  19. data/lib/rack/cache/context.rb +95 -0
  20. data/lib/rack/cache/core.rb +271 -0
  21. data/lib/rack/cache/entitystore.rb +224 -0
  22. data/lib/rack/cache/headers.rb +237 -0
  23. data/lib/rack/cache/metastore.rb +309 -0
  24. data/lib/rack/cache/options.rb +119 -0
  25. data/lib/rack/cache/request.rb +37 -0
  26. data/lib/rack/cache/response.rb +76 -0
  27. data/lib/rack/cache/storage.rb +50 -0
  28. data/lib/rack/utils/environment_headers.rb +78 -0
  29. data/rack-cache.gemspec +74 -0
  30. data/test/cache_test.rb +35 -0
  31. data/test/config_test.rb +66 -0
  32. data/test/context_test.rb +465 -0
  33. data/test/core_test.rb +84 -0
  34. data/test/entitystore_test.rb +176 -0
  35. data/test/environment_headers_test.rb +71 -0
  36. data/test/headers_test.rb +215 -0
  37. data/test/logging_test.rb +45 -0
  38. data/test/metastore_test.rb +210 -0
  39. data/test/options_test.rb +64 -0
  40. data/test/pony.jpg +0 -0
  41. data/test/response_test.rb +37 -0
  42. data/test/spec_setup.rb +189 -0
  43. data/test/storage_test.rb +94 -0
  44. metadata +120 -0
data/doc/events.dot ADDED
@@ -0,0 +1,27 @@
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
+ }
data/doc/faq.markdown ADDED
@@ -0,0 +1,133 @@
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='why-not-squid' href='#why-not-squid'>#</a>
14
+
15
+ ### Q: Why Rack::Cache? Why not Squid, Varnish, Perlbol, etc.?
16
+
17
+ __Rack::Cache__ is often easier to setup as part of your existing Ruby
18
+ application than a separate caching system. __Rack::Cache__ runs entirely inside
19
+ your backend application processes - no separate / external process is required.
20
+ This lets __Rack::Cache__ scale down to development environments and simple
21
+ deployments very easily while not sacrificing the benefits of a standards-based
22
+ approach to caching.
23
+
24
+
25
+ <a class='hash' id='why-not-rails' href='#why-not-rails'>#</a>
26
+
27
+ ### Q: Why Rack::Cache? Why not use Rails/Merb/FrameworkX's caching system?
28
+
29
+ __Rack::Cache__ takes a standards-based approach to caching that provides some
30
+ benefits over framework-integrated systems. It uses standard HTTP headers
31
+ (`Expires`, `Cache-Control`, `Etag`, `Last-Modified`, etc.) to determine
32
+ what/when to cache. Designing applications to support these standard HTTP
33
+ mechanisms gives the benefit of being able to switch to a different HTTP
34
+ cache implementation in the future.
35
+
36
+ In addition, using a standards-based approach to caching creates a clear
37
+ separation between application and caching logic. The application need only
38
+ specify a basic set of information about the response and all decisions
39
+ regarding how and when to cache is moved into the caching layer.
40
+
41
+
42
+ <a class='hash' id='scale' href='#scale'>#</a>
43
+
44
+ ### Q: Will Rack::Cache make my app scale?
45
+
46
+ No. Your design is the only thing that can make your app scale.
47
+
48
+ Also, __Rack::Cache__ is not overly optimized for performance. The main goal of
49
+ the project is to provide a portable, easy-to-configure, and standards-based
50
+ caching solution for small to medium sized deployments. More sophisticated /
51
+ performant caching systems (e.g., [Varnish][v], [Squid][s],
52
+ [httpd/mod-cache][h]) may be more appropriate for large deployments with
53
+ crazy-land throughput requirements.
54
+
55
+ [v]: http://varnish.projects.linpro.no/
56
+ [s]: http://www.squid-cache.org/
57
+ [h]: http://httpd.apache.org/docs/2.0/mod/mod_cache.html
58
+
59
+
60
+ Features
61
+ --------
62
+
63
+
64
+ <a class='hash' id='validation' href='#validation'>#</a>
65
+
66
+ ### Q: Does Rack::Cache support validation?
67
+
68
+ Yes. Both freshness and validation-based caching is supported. A response
69
+ will be cached if it has a freshness lifetime (e.g., `Expires` or
70
+ `Cache-Control: max-age=N` headers) and/or includes a validator (e.g.,
71
+ `Last-Modified` or `ETag` headers). When the cache hits and the response is
72
+ fresh, it's delivered immediately without talking to the backend application;
73
+ when the cache is stale, the cached response is validated using a conditional
74
+ GET request.
75
+
76
+
77
+ <a class='hash' id='fragments' href='#fragments'>#</a>
78
+
79
+ ### Q: Does Rack::Cache support fragment caching?
80
+
81
+ Not really. __Rack::Cache__ deals with entire responses and doesn't know
82
+ anything about how your application constructs them.
83
+
84
+ However, something like [ESI](http://www.w3.org/TR/esi-lang) may be implemented
85
+ in the future (likely as a separate Rack middleware component that could be
86
+ situated upstream from Rack::Cache), which would allow applications to compose
87
+ responses based on several "fragment resources". Each fragment would have its
88
+ own cache policy.
89
+
90
+
91
+ <a class='hash' id='manual-purge' href='#manual-purge'>#</a>
92
+
93
+ ### Q: How do I manually purge or expire a cached entry?
94
+
95
+ Although planned, there is currently no mechanism for manually purging
96
+ an entry stored in the cache.
97
+
98
+ Note that using an `Expires` or `Cache-Control: max-age=N` header and relying on
99
+ manual purge to invalidate cached entry can often be implemented more simply
100
+ using efficient validation based caching (`Last-Modified`, `Etag`). Many web
101
+ frameworks are based entirely on manual purge and do not support validation at
102
+ the cache level.
103
+
104
+
105
+ <a class='hash' id='efficient-validation' href='#efficient-validation'>#</a>
106
+
107
+ ### Q: What does "Efficient Validation" mean?
108
+
109
+ It means that your application performs only the processing necessary to
110
+ determine if a response is valid before sending a `304 Not Modified` in response
111
+ to a conditional GET request. Many applications that perform validation do so
112
+ only after the entire response has been generated, which provides bandwidth
113
+ savings but results in no CPU/IO savings. Implementing validation efficiently
114
+ can increase backend application throughput significantly when fronted by a
115
+ validating caching system (like __Rack::Cache__).
116
+
117
+ [Here's an example Rack application](http://gist.github.com/9395) that performs
118
+ efficient validation.
119
+
120
+
121
+ <a class='hash' id='orly' href='#orly'>#</a>
122
+
123
+ ### Q: Did you just make that up?
124
+
125
+ Yes.
126
+
127
+
128
+ <a class='hash' id='https' href='#https'>#</a>
129
+
130
+ ### Q: Can I do HTTPS with Rack::Cache?
131
+
132
+ Sure. HTTPS is typically managed by a front-end web server so this isn't really
133
+ relevant to Rack::Cache.
@@ -0,0 +1,113 @@
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 ([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
+ * [Configuration language][config] for advanced caching policies.
11
+ * Disk, memcached, and heap memory [storage backends][storage].
12
+
13
+ Status
14
+ ------
15
+
16
+ __Rack::Cache__ is a young and experimental project that is likely to
17
+ change substantially and may not be wholly functional, consistent,
18
+ fast, or correct. The current focus is on reaching basic compliance
19
+ with RFC 2616 and providing good documentation.
20
+
21
+ Installation
22
+ ------------
23
+
24
+ $ sudo gem install rack-cache
25
+
26
+ Or, from a local working copy:
27
+
28
+ $ git clone git://github.com/rtomayko/rack-cache.git
29
+ $ rake package && sudo rake install
30
+
31
+ Basic Usage
32
+ -----------
33
+
34
+ __Rack::Cache__ is implemented as a piece of [Rack][] middleware and can be used
35
+ with any __Rack__-based application. If your application includes a rackup
36
+ (`.ru`) file or uses __Rack::Builder__ to construct the application pipeline,
37
+ simply `require` and `use` as follows:
38
+
39
+ require 'rack/cache'
40
+
41
+ use Rack::Cache,
42
+ :verbose => true,
43
+ :metastore => 'file:/var/cache/rack/meta'
44
+ :entitystore => 'file:/var/cache/rack/body'
45
+
46
+ run app
47
+
48
+ Assuming you've designed your backend application to take advantage of HTTP's
49
+ caching features, no further code or configuration is required for basic
50
+ caching. More sophisticated stuff is possible with [Rack::Cache's Configuration
51
+ Language][config].
52
+
53
+ Advanced Usage
54
+ --------------
55
+
56
+ * [Configuration Language Documentation][config] - How to customize cache
57
+ policy using the simple event-based configuration system.
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
+ * [FAQ](./faq) - Frequently Asked Questions about __Rack::Cache__.
64
+
65
+ * [GitHub Repository](http://github.com/rtomayko/rack-cache/) - Get your
66
+ fork on.
67
+
68
+ * [RDoc API Documentation](./api/) - Mostly worthless if you just want to use
69
+ __Rack::Cache__ in your application but mildly insightful if you'd like to
70
+ get a feel for how the system has been put together; I recommend
71
+ [reading the source](http://github.com/rtomayko/rack-cache/master/lib/rack/cache).
72
+
73
+ See Also
74
+ --------
75
+
76
+ The overall design of __Rack::Cache__ is based largely on the work of the
77
+ internet standards community. The following resources provide a good starting
78
+ point for exploring the basic concepts of HTTP caching:
79
+
80
+ * Mark Nottingham's [Caching Tutorial](http://www.mnot.net/cache_docs/),
81
+ especially the short section on
82
+ [How Web Caches Work](http://www.mnot.net/cache_docs/#WORK)
83
+
84
+ * Joe Gregorio's [Doing HTTP Caching Right](http://www.xml.com/lpt/a/1642)
85
+
86
+ * [RFC 2616](http://www.ietf.org/rfc/rfc2616.txt), especially
87
+ [Section 13, "Caching in HTTP"](http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html)
88
+
89
+ __Rack::Cache__ takes (_liberally_) various concepts from
90
+ [Varnish](http://varnish.projects.linpro.no/) and
91
+ [Django's cache framework](http://docs.djangoproject.com/en/dev/topics/cache/).
92
+
93
+ License
94
+ -------
95
+
96
+ __Rack::Cache__ is Copyright &copy; 2008
97
+ by [Ryan Tomayko](http://tomayko.com/about)
98
+ and is provided under [the MIT license](./license)
99
+
100
+ [config]: ./configuration "Rack::Cache Configuration Language Documentation"
101
+ [storage]: ./storage "Rack::Cache Storage Documentation"
102
+
103
+ [rfc]: http://tools.ietf.org/html/rfc2616
104
+ "RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1 [ietf.org]"
105
+
106
+ [s13]: http://tools.ietf.org/html/rfc2616#section-13
107
+ "RFC 2616 / Section 13 Caching in HTTP"
108
+
109
+ [rack]: http://rack.rubyforge.org/
110
+ "Rack: a Ruby Webserver Interface"
111
+
112
+ [vcl]: http://tomayko.com/man/vcl
113
+ "VCL(7) -- Varnish Configuration Language Manual Page"
@@ -0,0 +1,33 @@
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="./faq" title='Frequently Asked Questions'>FAQ</a> |
18
+ <a href="./api/" title='Fucking Sucks.'>RDoc</a>
19
+ </p>
20
+ </div>
21
+ <div id='content'><%= content %></div>
22
+ <div id='footer'>
23
+ <p class='rights'>
24
+ Copyright
25
+ <a href="./license" rel="license">&copy;</a>
26
+ 2003-2008
27
+ by
28
+ <a href='http://tomayko.com/about' rel='me author'>Ryan Tomayko</a>
29
+ </p>
30
+ </div>
31
+ </div>
32
+ </body>
33
+ </html>
@@ -0,0 +1,24 @@
1
+ License (MIT)
2
+ =============
3
+
4
+ __Rack::Cache__ is Copyright &copy; 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>
@@ -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: */