[Checkins] SVN: hurry.resource/branches/janjaapdriessen-resource-publisher/ Add tests for publisher, explain hashes in URLs. Update changelog
Jan-Jaap Driessen
jdriessen at thehealthagency.com
Tue Nov 16 10:19:11 EST 2010
Log message for revision 118430:
Add tests for publisher, explain hashes in URLs. Update changelog
Changed:
U hurry.resource/branches/janjaapdriessen-resource-publisher/CHANGES.txt
U hurry.resource/branches/janjaapdriessen-resource-publisher/src/hurry/resource/README.txt
-=-
Modified: hurry.resource/branches/janjaapdriessen-resource-publisher/CHANGES.txt
===================================================================
--- hurry.resource/branches/janjaapdriessen-resource-publisher/CHANGES.txt 2010-11-16 15:18:14 UTC (rev 118429)
+++ hurry.resource/branches/janjaapdriessen-resource-publisher/CHANGES.txt 2010-11-16 15:19:11 UTC (rev 118430)
@@ -19,11 +19,15 @@
- Add hashing functionality that calculates a checksum of a resource library.
-- Hurry.resource.publisher knows about publishing resources.
+- Hurry.resource.wsgi knows about publishing resources.
- The hurry.resource.wsgi injector middleware informs the wrapped application
of the presence of a resource publisher.
+- Do not serve hidden files in hurry.resource.publisher.
+
+- Publisher only sets cache control header on successful responses.
+
0.10 (2010-07-24)
=================
Modified: hurry.resource/branches/janjaapdriessen-resource-publisher/src/hurry/resource/README.txt
===================================================================
--- hurry.resource/branches/janjaapdriessen-resource-publisher/src/hurry/resource/README.txt 2010-11-16 15:18:14 UTC (rev 118429)
+++ hurry.resource/branches/janjaapdriessen-resource-publisher/src/hurry/resource/README.txt 2010-11-16 15:19:11 UTC (rev 118430)
@@ -31,19 +31,21 @@
A resource library
==================
-We define a library ``foo``. It takes two arguments, the name of the
-library as it should be published under in a URL and uniquely identify
+A hurry.resource Library takes two arguments: the name of the library
+as it should be published under in a URL and uniquely identify
it, and a path to the root of the resources (rootpath) that this
-library publishes::
+library publishes. In the ``mypackage``, which was installed during
+setup of this test, we define a resource ``foo``::
- >>> from hurry.resource import Library
- >>> foo = Library('foo', 'dummy')
+ >>> from mypackage import foo
+ >>> foo.name
+ 'foo'
The full path to the directory with the resources is reconstructed from
the package that the Library is defined in::
- >>> foo.path #doctest: +ELLIPSIS
- '.../hurry.resource/src/hurry/resource/dummy'
+ >>> foo.path
+ '...test-installs/mypackage-1.0dev.../mypackage/resources'
Entry points
============
@@ -65,16 +67,9 @@
There is an API to help you obtain all registered libraries::
>>> from hurry.resource import libraries
-
-Nothing is registered however::
-
>>> list(libraries())
- []
+ [<hurry.resource.core.Library object at ...>]
-It would be nice to now have some tests that see whether entry points
-actually get picked up so, but that would require an involved test
-setup that we find difficult to construct.
-
Inclusion
=========
@@ -110,7 +105,7 @@
Let's now see what resources are needed by this inclusion::
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'b.css' in library 'foo'>,
<ResourceInclusion 'a.js' in library 'foo'>,
<ResourceInclusion 'c.js' in library 'foo'>]
@@ -130,7 +125,7 @@
>>> needed = NeededInclusions()
>>> needed.need(group)
- >>> group.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> group.inclusions()
[<ResourceInclusion 'a.js' in library 'foo'>,
<ResourceInclusion 'b.css' in library 'foo'>]
@@ -138,7 +133,7 @@
inclusions directly::
>>> more_stuff = ResourceInclusion(foo, 'more_stuff.js', depends=[group])
- >>> more_stuff.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> more_stuff.inclusions()
[<ResourceInclusion 'a.js' in library 'foo'>,
<ResourceInclusion 'b.css' in library 'foo'>,
<ResourceInclusion 'more_stuff.js' in library 'foo'>]
@@ -216,7 +211,7 @@
The resource inclusion will now indeed be needed::
- >>> get_current_needed_inclusions().inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> get_current_needed_inclusions().inclusions()
[<ResourceInclusion 'b.css' in library 'foo'>,
<ResourceInclusion 'a.js' in library 'foo'>,
<ResourceInclusion 'c.js' in library 'foo'>]
@@ -278,7 +273,7 @@
We need ``y1`` again::
>>> needed.need(y1)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'b.css' in library 'foo'>,
<ResourceInclusion 'a.js' in library 'foo'>,
<ResourceInclusion 'c.js' in library 'foo'>]
@@ -288,7 +283,7 @@
in the needed resources::
>>> needed.need(y1)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'b.css' in library 'foo'>,
<ResourceInclusion 'a.js' in library 'foo'>,
<ResourceInclusion 'c.js' in library 'foo'>]
@@ -297,12 +292,12 @@
already required ``x1`` and ``x2``::
>>> needed.need(x1)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'b.css' in library 'foo'>,
<ResourceInclusion 'a.js' in library 'foo'>,
<ResourceInclusion 'c.js' in library 'foo'>]
>>> needed.need(x2)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'b.css' in library 'foo'>,
<ResourceInclusion 'a.js' in library 'foo'>,
<ResourceInclusion 'c.js' in library 'foo'>]
@@ -314,7 +309,7 @@
>>> needed.need(x1)
>>> needed.need(x2)
>>> needed.need(y1)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'b.css' in library 'foo'>,
<ResourceInclusion 'a.js' in library 'foo'>,
<ResourceInclusion 'c.js' in library 'foo'>]
@@ -327,12 +322,12 @@
>>> a3 = ResourceInclusion(foo, 'a3.js', depends=[a2])
>>> a4 = ResourceInclusion(foo, 'a4.js', depends=[a1])
>>> needed.need(a3)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'a1.js' in library 'foo'>,
<ResourceInclusion 'a2.js' in library 'foo'>,
<ResourceInclusion 'a3.js' in library 'foo'>]
>>> needed.need(a4)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'a1.js' in library 'foo'>,
<ResourceInclusion 'a2.js' in library 'foo'>,
<ResourceInclusion 'a3.js' in library 'foo'>,
@@ -344,7 +339,7 @@
>>> needed = NeededInclusions()
>>> needed.need(a4)
>>> needed.need(a3)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'a1.js' in library 'foo'>,
<ResourceInclusion 'a4.js' in library 'foo'>,
<ResourceInclusion 'a2.js' in library 'foo'>,
@@ -356,7 +351,7 @@
>>> a5 = ResourceInclusion(foo, 'a5.js', depends=[a4, a3])
>>> needed = NeededInclusions()
>>> needed.need(a5)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'a1.js' in library 'foo'>,
<ResourceInclusion 'a4.js' in library 'foo'>,
<ResourceInclusion 'a2.js' in library 'foo'>,
@@ -370,7 +365,7 @@
>>> needed = NeededInclusions()
>>> needed.need(a3)
>>> needed.need(a5)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'a1.js' in library 'foo'>,
<ResourceInclusion 'a2.js' in library 'foo'>,
<ResourceInclusion 'a3.js' in library 'foo'>,
@@ -393,14 +388,14 @@
By default, we get ``k.js``::
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'k.js' in library 'foo'>]
We can however also get the resource for mode ``debug`` and get
``k-debug.js``::
>>> needed.mode('debug')
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'k-debug.js' in library 'foo'>]
Modes can also be specified fully with a resource inclusion, which allows
@@ -413,14 +408,14 @@
By default we get ``k2.js``::
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'k2.js' in library 'foo'>]
We can however also get the resource for mode ``debug`` and get
``k2-debug.js``::
>>> needed.mode('debug')
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'k2-debug.js' in library 'foo'>]
Note that modes are assumed to be identical in dependency structure;
@@ -432,7 +427,7 @@
>>> needed = NeededInclusions()
>>> needed.mode('minified')
>>> needed.need(k1)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'k.js' in library 'foo'>]
``hurry.resource`` suggests resource libraries follow the following
@@ -477,7 +472,7 @@
Let's look at the resources needed by default::
>>> c = get_current_needed_inclusions()
- >>> c.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> c.inclusions()
[<ResourceInclusion 'l1.js' in library 'foo'>]
Let's now change the mode using the convenience
@@ -488,7 +483,7 @@
When we request the resources now, we get them in the ``debug`` mode::
- >>> c.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> c.inclusions()
[<ResourceInclusion 'l1-debug.js' in library 'foo'>]
"Rollups"
@@ -514,7 +509,7 @@
>>> needed = NeededInclusions()
>>> needed.need(b1)
>>> needed.need(b2)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'b1.js' in library 'foo'>, <ResourceInclusion 'b2.js' in library 'foo'>]
Let's enable rollups::
@@ -532,7 +527,7 @@
>>> needed.need(b1)
>>> needed.need(b2)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'giant.js' in library 'foo'>]
The system will by default only consolidate exactly. That is, if only a single
@@ -541,7 +536,7 @@
>>> needed = NeededInclusions()
>>> needed.rollup()
>>> needed.need(b1)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'b1.js' in library 'foo'>]
Let's look at this with a larger consolidation of 3 resources::
@@ -556,7 +551,7 @@
>>> needed = NeededInclusions()
>>> needed.rollup()
>>> needed.need(c1)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'c1.css' in library 'foo'>]
Neither will it roll up two resources::
@@ -565,7 +560,7 @@
>>> needed.rollup()
>>> needed.need(c1)
>>> needed.need(c2)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'c1.css' in library 'foo'>,
<ResourceInclusion 'c2.css' in library 'foo'>]
@@ -576,7 +571,7 @@
>>> needed.need(c1)
>>> needed.need(c2)
>>> needed.need(c3)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'giantc.css' in library 'foo'>]
The default behavior is to play it safe: we cannot be certain that we
@@ -606,7 +601,7 @@
>>> needed.rollup()
>>> needed.need(d1)
>>> needed.need(d2)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'giantd.js' in library 'foo'>]
In fact even if we only need a single resource the eager superseder will
@@ -615,7 +610,7 @@
>>> needed = NeededInclusions()
>>> needed.rollup()
>>> needed.need(d1)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'giantd.js' in library 'foo'>]
If there are two potential eager superseders, the biggest one will
@@ -628,7 +623,7 @@
>>> needed.rollup()
>>> needed.need(d1)
>>> needed.need(d2)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'giantd-bigger.js' in library 'foo'>]
If there is a potential non-eager superseder and an eager one, the eager one
@@ -642,7 +637,7 @@
>>> needed.need(d2)
>>> needed.need(d3)
>>> needed.need(d4)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'giantd-bigger.js' in library 'foo'>]
A resource can be part of multiple rollups. In this case the rollup
@@ -662,7 +657,7 @@
>>> needed.need(e1)
>>> needed.need(e2)
>>> needed.need(e3)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'giante-three.js' in library 'foo'>]
Consolidation also works with modes::
@@ -676,10 +671,10 @@
>>> needed.rollup()
>>> needed.need(f1)
>>> needed.need(f2)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'giantf.js' in library 'foo'>]
>>> needed.mode('debug')
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'giantf-debug.js' in library 'foo'>]
What if the rolled up resources have no mode but the superseding resource
@@ -694,10 +689,10 @@
>>> needed.rollup()
>>> needed.need(g1)
>>> needed.need(g2)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'giantg.js' in library 'foo'>]
>>> needed.mode('debug')
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'giantg.js' in library 'foo'>]
What if the rolled up resources have a mode but the superseding resource
@@ -710,14 +705,14 @@
>>> needed.rollup()
>>> needed.need(h1)
>>> needed.need(h2)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'gianth.js' in library 'foo'>]
Since there is no superseder for the debug mode, we will get the two
resources, not rolled up::
>>> needed.mode('debug')
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'h1-debug.js' in library 'foo'>,
<ResourceInclusion 'h2-debug.js' in library 'foo'>]
@@ -728,7 +723,7 @@
>>> needed = NeededInclusions()
>>> needed.need(y1)
- >>> needed.inclusions() #doctest: +NORMALIZE_WHITESPACE
+ >>> needed.inclusions()
[<ResourceInclusion 'b.css' in library 'foo'>,
<ResourceInclusion 'a.js' in library 'foo'>,
<ResourceInclusion 'c.js' in library 'foo'>]
@@ -740,7 +735,6 @@
...
AttributeError: 'NoneType' object has no attribute 'endswith'
-
That didn't work. In order to render an inclusion, we need to tell
``hurry.resource`` the base URL for a resource inclusion. We
already know the relative URL, so we need to specify how to get a URL
@@ -759,9 +753,9 @@
for instance)::
>>> print needed.render()
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
Let's set this a currently needed inclusions::
@@ -772,10 +766,59 @@
>>> from hurry import resource
>>> print resource.render()
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
+Hashing resources
+=================
+
+As you have seen in the rendered URLs above, we insert a ``hash:`` segment
+into the URLs. Hashing of resources is inspired by z3c.hashedresource:
+
+'While we want browsers to cache static resources such as CSS-stylesheets and
+JavaScript files, we also want them *not* to use the cached version if the
+files on the server have been updated. (And we don't want to make end-users
+have to empty their browser cache to get the latest version. Nor explain how
+to do that over the phone every time.)'
+
+To make browsers update their caches of resources immediately when the
+resource changes, the absolute URLs of resources can now be made to contain a
+hash of the resource's contents, so it will look like
+/foo/hash:12345/myresource instead of /foo/myresource.
+
+ >>> before_hash = foo.hash()
+ >>> from pkg_resources import resource_filename, resource_string
+ >>> before = resource_string('mypackage', 'resources/style.css')
+ >>> mypackage_style = resource_filename('mypackage', 'resources/style.css')
+ >>> open(mypackage_style, 'w').write('body {color: #0f0;}')
+ >>> foo.hash() == before_hash
+ False
+
+ >>> # Reset the content.
+ >>> open(mypackage_style, 'w').write(before)
+ >>> foo.hash() == before_hash
+ True
+
+Any VCS directories are ignores in calculating the hash:
+
+ >>> import os
+ >>> os.mkdir(resource_filename('mypackage',
+ ... os.path.join('resources', 'sub')))
+ >>> os.mkdir(resource_filename('mypackage',
+ ... os.path.join('resources', 'sub', '.svn')))
+ >>> open(os.path.join(resource_filename('mypackage', 'resources/sub/.svn'), 'test'),
+ ... 'w').write('test')
+ >>> foo.hash() == before_hash
+ True
+
+In developer mode the hash is recomputed each time the resource is asked for
+its URL, while in production mode the hash is computed only once, so remember
+to restart the server after changing resource files (else browsers will still
+see the old URL unchanged and use their outdated cached versions of the files).
+
+ XXX Add tests for dev mode and non dev mode.
+
Inserting resources in HTML
===========================
@@ -791,9 +834,9 @@
>>> print needed.render_into_html(html)
<html><head>
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
something more</head></html>
The top-level convenience function does this for the currently needed
@@ -801,9 +844,9 @@
>>> print resource.render_into_html(html)
<html><head>
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:...b.css" />
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
something more</head></html>
See below for a way to insert into HTML when bottom fragments are
@@ -824,9 +867,9 @@
>>> top, bottom = needed.render_topbottom()
>>> print top
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
>>> print bottom
<BLANKLINE>
@@ -841,9 +884,9 @@
>>> top, bottom = needed.render_topbottom()
>>> print top
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
>>> print bottom
<BLANKLINE>
@@ -853,10 +896,10 @@
>>> needed.bottom(force=True)
>>> top, bottom = needed.render_topbottom()
>>> print top
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
>>> print bottom
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
Let's now introduce a javascript resource that says it is safe to be
included on the bottom::
@@ -873,10 +916,10 @@
>>> top, bottom = needed.render_topbottom()
>>> print top
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/y2.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../y2.js"></script>
>>> print bottom
<BLANKLINE>
@@ -888,22 +931,22 @@
>>> top, bottom = needed.render_topbottom()
>>> print top
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
>>> print bottom
- <script type="text/javascript" src="http://localhost/static/foo/y2.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../y2.js"></script>
There's also a convenience function for the currently needed inclusion::
>>> request.needed = needed
>>> top, bottom = resource.render_topbottom()
>>> print top
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
>>> print bottom
- <script type="text/javascript" src="http://localhost/static/foo/y2.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../y2.js"></script>
When we force bottom rendering of Javascript, there is no effect of
making a resource bottom-safe: all ``.js`` resources will be rendered
@@ -912,11 +955,11 @@
>>> needed.bottom(force=True)
>>> top, bottom = needed.render_topbottom()
>>> print top
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
>>> print bottom
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/y2.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../y2.js"></script>
Note that if ``bottom`` is enabled, it makes no sense to have a
resource inclusion ``b`` that depends on a resource inclusion ``a``
@@ -947,20 +990,20 @@
>>> print needed.render_topbottom_into_html(html)
<html><head>
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- rest of head</head><body>rest of body<script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/y2.js"></script></body></html>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
+ rest of head</head><body>rest of body<script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../y2.js"></script></body></html>
There's also a function available to do this for the currently needed
resources::
>>> print resource.render_topbottom_into_html(html)
<html><head>
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- rest of head</head><body>rest of body<script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/y2.js"></script></body></html>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
+ rest of head</head><body>rest of body<script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../y2.js"></script></body></html>
Using WSGI middleware to insert into HTML
=========================================
@@ -995,9 +1038,9 @@
>>> print res.body
<html><head>
- <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/b.css" />
- <script type="text/javascript" src="http://localhost/static/foo/a.js"></script>
- <script type="text/javascript" src="http://localhost/static/foo/c.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://localhost/static/foo/hash:.../b.css" />
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../a.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../c.js"></script>
</head><body</body></html>
When we set the response Content-Type to non-HTML, the middleware
@@ -1031,7 +1074,7 @@
>>> c.base_url = 'http://localhost/static'
>>> top, bottom = c.render_topbottom()
>>> print top
- <script type="text/javascript" src="http://localhost/static/foo/l1.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../l1.js"></script>
>>> print bottom
<BLANKLINE>
@@ -1047,7 +1090,7 @@
>>> print top
<BLANKLINE>
>>> print bottom
- <script type="text/javascript" src="http://localhost/static/foo/l1.js"></script>
+ <script type="text/javascript" src="http://localhost/static/foo/hash:.../l1.js"></script>
Generating resource code
========================
@@ -1067,7 +1110,7 @@
>>> print generate_code(i1=i1, i2=i2, i3=i3, i4=i4, i5=i5)
from hurry.resource import Library, ResourceInclusion
<BLANKLINE>
- foo = Library('foo', 'dummy')
+ foo = Library('foo', 'resources')
<BLANKLINE>
i1 = ResourceInclusion(foo, 'i1.js')
i2 = ResourceInclusion(foo, 'i2.js', depends=[i1])
@@ -1085,7 +1128,7 @@
>>> print generate_code(j1=j1, j2=j2, giantj=giantj)
from hurry.resource import Library, ResourceInclusion
<BLANKLINE>
- foo = Library('foo', 'dummy')
+ foo = Library('foo', 'resources')
<BLANKLINE>
j1 = ResourceInclusion(foo, 'j1.js', debug='j1-debug.js')
j2 = ResourceInclusion(foo, 'j2.js', debug='j2-debug.js')
@@ -1097,14 +1140,14 @@
>>> print generate_code(hoi=i1)
from hurry.resource import Library, ResourceInclusion
<BLANKLINE>
- foo = Library('foo', 'dummy')
+ foo = Library('foo', 'resources')
<BLANKLINE>
hoi = ResourceInclusion(foo, 'i1.js')
>>> print generate_code(hoi=i1, i2=i2)
from hurry.resource import Library, ResourceInclusion
<BLANKLINE>
- foo = Library('foo', 'dummy')
+ foo = Library('foo', 'resources')
<BLANKLINE>
hoi = ResourceInclusion(foo, 'i1.js')
i2 = ResourceInclusion(foo, 'i2.js', depends=[hoi])
@@ -1123,7 +1166,7 @@
Let's make a list of resource inclusions not sorted by dependency::
>>> i = [a5, a3, a1, a2, a4]
- >>> sort_inclusions_topological(i) #doctest: +NORMALIZE_WHITESPACE
+ >>> sort_inclusions_topological(i)
[<ResourceInclusion 'a1.js' in library 'foo'>,
<ResourceInclusion 'a4.js' in library 'foo'>,
<ResourceInclusion 'a2.js' in library 'foo'>,
@@ -1153,7 +1196,7 @@
>>> a6 = ResourceInclusion(foo, 'nothing.unknown')
>>> from hurry.resource.core import render_inclusions
- >>> render_inclusions([a6], '/') #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+ >>> render_inclusions([a6], '/')
Traceback (most recent call last):
...
UnknownResourceExtension: Unknown resource extension .unknown for resource
@@ -1167,4 +1210,58 @@
>>> inclusion_renderers['.unknown'] = render_unknown
>>> render_inclusions([a6], 'http://localhost/static/')
- '<link rel="unknown" href="http://localhost/static/foo/nothing.unknown" />'
+ '<link rel="unknown" href="http://localhost/static/foo/hash:.../nothing.unknown" />'
+
+
+Resource publisher
+==================
+
+The hurry.resource.publisher is a WSGI component that publishes registered
+resource libraries.
+
+ >>> from hurry.resource.publisher import Publisher
+ >>> from paste.fixture import TestApp
+ >>> app = TestApp(Publisher())
+
+We don't do anything fancy if the resource can not be found, but raise 404.
+
+ >>> res = app.get('/', expect_errors=True)
+ >>> res.status
+ 404
+
+The resources are handled by paste.fileapp.DirectoryApp, which sets the
+ETag header, among other things::
+
+ >>> res = app.get('/foo/style.css')
+ >>> print res.body
+ body {
+ color: #f00;
+ }
+ >>> headers = dict(res.headers)
+ >>> 'ETag' in headers
+ True
+
+When we find the 'hash' marker in the requested URL, we send headers that let
+the user agent cache the resources for a long time.
+
+ >>> res = app.get('/foo/hash:12345/style.css')
+ >>> headers = dict(res.headers)
+ >>> 'Expires' in headers
+ True
+ >>> print headers['Cache-Control']
+ public, max-age=314496000
+
+We don't set cache-control headers on non-successful responses::
+
+ >>> res = app.get('/foo/hash:12345/notfound.css', expect_errors=True)
+ >>> headers = dict(res.headers)
+ >>> 'Expires' in headers
+ False
+ >>> 'Cache-Control' in headers
+ False
+
+Hidden files and directories are not served:
+
+ >>> res = app.get('/foo/sub/.svn/test', expect_errors=True)
+ >>> print res.status
+ 404
More information about the checkins
mailing list