[Checkins] SVN: z3c.hashedresource/trunk/ initial import

Wolfgang Schnerring wosc at wosc.de
Mon Jun 15 07:57:58 EDT 2009


Log message for revision 100984:
  initial import
  

Changed:
  A   z3c.hashedresource/trunk/.installed.cfg
  A   z3c.hashedresource/trunk/CHANGES.txt
  A   z3c.hashedresource/trunk/bootstrap.py
  A   z3c.hashedresource/trunk/buildout.cfg
  A   z3c.hashedresource/trunk/setup.py
  A   z3c.hashedresource/trunk/src/
  A   z3c.hashedresource/trunk/src/z3c/
  A   z3c.hashedresource/trunk/src/z3c/__init__.py
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/README.txt
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/__init__.py
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/configure.zcml
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/ftesting.zcml
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/hash.py
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/interfaces.py
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/testing.py
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/tests/
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/tests/__init__.py
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/tests/fixture/
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/tests/fixture/test.pt
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/tests/fixture/test.txt
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_browser.py
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_hash.py
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_url.py
  A   z3c.hashedresource/trunk/src/z3c/hashedresource/url.py

-=-
Added: z3c.hashedresource/trunk/.installed.cfg
===================================================================
--- z3c.hashedresource/trunk/.installed.cfg	                        (rev 0)
+++ z3c.hashedresource/trunk/.installed.cfg	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,63 @@
+[buildout]
+installed_develop_eggs = 
+parts = test wpy
+
+[test]
+__buildout_installed__ = /home/wosc/gocept/zope/z3c.hashedresource/parts/test
+	/home/wosc/gocept/zope/z3c.hashedresource/bin/test
+__buildout_signature__ = zc.recipe.testrunner-1.2.0-py2.5.egg zc.recipe.egg-1.2.2-py2.5.egg setuptools-0.6c9-py2.5.egg zope.testing-3.7.5-py2.5.egg zc.buildout-1.2.1-py2.5.egg zope.interface-3.5.1-py2.5-linux-i686.egg
+_b = /home/wosc/gocept/zope/z3c.hashedresource/bin
+_d = /home/wosc/gocept/zope/z3c.hashedresource/develop-eggs
+_e = /home/wosc/.python-eggs
+bin-directory = /home/wosc/gocept/zope/z3c.hashedresource/bin
+develop-eggs-directory = /home/wosc/gocept/zope/z3c.hashedresource/develop-eggs
+eggs = z3c.hashedresource[test]
+eggs-directory = /home/wosc/.python-eggs
+executable = /usr/local/python2.5/bin/python
+location = /home/wosc/gocept/zope/z3c.hashedresource/parts/test
+recipe = zc.recipe.testrunner
+script = /home/wosc/gocept/zope/z3c.hashedresource/bin/test
+
+[wpy]
+__buildout_installed__ = /home/wosc/gocept/zope/z3c.hashedresource/bin/wpy
+__buildout_signature__ = zc.recipe.egg-1.2.2-py2.5.egg setuptools-0.6c9-py2.5.egg zc.buildout-1.2.1-py2.5.egg
+_b = /home/wosc/gocept/zope/z3c.hashedresource/bin
+_d = /home/wosc/gocept/zope/z3c.hashedresource/develop-eggs
+_e = /home/wosc/.python-eggs
+bin-directory = /home/wosc/gocept/zope/z3c.hashedresource/bin
+develop-eggs-directory = /home/wosc/gocept/zope/z3c.hashedresource/develop-eggs
+eggs = z3c.hashedresource[test]
+eggs-directory = /home/wosc/.python-eggs
+executable = /usr/local/python2.5/bin/python
+interpreter = wpy
+recipe = zc.recipe.egg
+
+[buildout]
+installed_develop_eggs = 
+
+[buildout]
+parts = test
+
+[buildout]
+installed_develop_eggs = 
+
+[buildout]
+parts = test
+
+[buildout]
+installed_develop_eggs = 
+
+[buildout]
+parts = test
+
+[buildout]
+installed_develop_eggs = 
+
+[buildout]
+parts = test
+
+[buildout]
+installed_develop_eggs = 
+
+[buildout]
+parts = test

Added: z3c.hashedresource/trunk/CHANGES.txt
===================================================================
--- z3c.hashedresource/trunk/CHANGES.txt	                        (rev 0)
+++ z3c.hashedresource/trunk/CHANGES.txt	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,8 @@
+=======
+CHANGES
+=======
+
+0.1 (unreleased)
+================
+
+- first released version

Added: z3c.hashedresource/trunk/bootstrap.py
===================================================================
--- z3c.hashedresource/trunk/bootstrap.py	                        (rev 0)
+++ z3c.hashedresource/trunk/bootstrap.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,55 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+try:
+    import pkg_resources
+except ImportError:
+    ez = {}
+    exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                         ).read() in ez
+    ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+    import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)

Added: z3c.hashedresource/trunk/buildout.cfg
===================================================================
--- z3c.hashedresource/trunk/buildout.cfg	                        (rev 0)
+++ z3c.hashedresource/trunk/buildout.cfg	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,7 @@
+[buildout]
+develop = .
+parts = test
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.hashedresource[test]

Added: z3c.hashedresource/trunk/setup.py
===================================================================
--- z3c.hashedresource/trunk/setup.py	                        (rev 0)
+++ z3c.hashedresource/trunk/setup.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,43 @@
+import os
+from setuptools import setup, find_packages
+
+
+setup(
+    name='z3c.hashedresource',
+    version = '1.0dev',
+    author='Wolfgang Schnerring',
+    author_email='ws at gocept.com',
+    description='Provides URLs for resources that change whenever their content changes.',
+    url='http://pypi.python.org/pypi/z3c.hashedresource',
+    long_description= (
+        open(os.path.join('src', 'z3c', 'hashedresource', 'README.txt')).read()
+        + '\n\n'
+        + open('CHANGES.txt').read()),
+    classifiers = [
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Zope Public License',
+        'Programming Language :: Python',
+        'Operating System :: OS Independent',
+        'Framework :: Zope3'],
+    license='ZPL 2.1',
+    packages=find_packages('src'),
+    package_dir = {'': 'src'},
+    namespace_packages=['z3c'],
+    install_requires=[
+        'setuptools',
+        'z3c.noop',
+        'zope.component',
+        'zope.interface',
+        'zope.app.publisher',
+        ],
+    extras_require=dict(test=[
+        'zope.app.testing',
+        'zope.app.zcmlfiles',
+        'zope.publisher',
+        'zope.security',
+        'zope.site',
+        'zope.testbrowser',
+        ]),
+    include_package_data = True,
+    zip_safe = False,
+)

Added: z3c.hashedresource/trunk/src/z3c/__init__.py
===================================================================
--- z3c.hashedresource/trunk/src/z3c/__init__.py	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/__init__.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,5 @@
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError, e:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/README.txt
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/README.txt	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/README.txt	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,20 @@
+==================
+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
+/++noop++12345/@@/myresource instead of /@@/myresource.
+
+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).
+

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/__init__.py
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/__init__.py	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/__init__.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1 @@
+# python package

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/configure.zcml
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/configure.zcml	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/configure.zcml	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,27 @@
+<configure
+  xmlns="http://namespaces.zope.org/zope"
+  xmlns:zcml="http://namespaces.zope.org/zcml"
+  >
+
+  <include package="z3c.noop"/>
+
+  <adapter
+    factory=".url.HashingURL"
+    />
+  <adapter zcml:condition="have devmode"
+    factory=".hash.ContentsHash"
+    for="zope.app.publisher.browser.directoryresource.DirectoryResource"
+    />
+  <adapter zcml:condition="have devmode"
+    factory=".hash.ContentsHash"
+    for="zope.app.publisher.browser.fileresource.FileResource"
+    />
+  <adapter zcml:condition="not-have devmode"
+    factory=".hash.CachingContentsHash"
+    for="zope.app.publisher.browser.directoryresource.DirectoryResource"
+    />
+  <adapter zcml:condition="not-have devmode"
+    factory=".hash.CachingContentsHash"
+    for="zope.app.publisher.browser.fileresource.FileResource"
+    />
+</configure>
\ No newline at end of file

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/ftesting.zcml
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/ftesting.zcml	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/ftesting.zcml	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,16 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:browser="http://namespaces.zope.org/browser"
+   >
+
+
+  <include package="zope.app.zcmlfiles" />
+  <include package="zope.app.publisher" />
+  <include package="z3c.hashedresource" />
+
+  <!-- example resource for testing -->
+  <browser:resourceDirectory
+    directory="tests/fixture"
+    name="myresource"
+    />
+</configure>
\ No newline at end of file

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/hash.py
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/hash.py	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/hash.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,61 @@
+#############################################################################
+#
+# Copyright (c) 2006-2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+from z3c.hashedresource import interfaces
+from zope.interface import implements, implementsOnly
+import md5
+import os
+
+
+class ContentsHash(object):
+
+    implements(interfaces.IResourceContentsHash)
+
+    def __init__(self, context):
+        self.context = context
+
+    def __str__(self):
+        path = self.context.context.path
+        if os.path.isdir(path):
+            files = self._list_directory(path)
+        else:
+            files = [path]
+
+        result = md5.new()
+        for file in files:
+            f = open(file, 'rb')
+            data = f.read()
+            f.close()
+            result.update(data)
+        result = result.hexdigest()
+        return result
+
+    def _list_directory(self, path):
+        for root, dirs, files in os.walk(path):
+            for file in files:
+                yield os.path.join(root, file)
+
+
+_contents_hash = {}
+
+class CachingContentsHash(ContentsHash):
+
+    def __str__(self):
+        path = self.context.context.path
+        try:
+            return _contents_hash[path]
+        except KeyError:
+            result = super(CachingContentsHash, self).__str__()
+            _contents_hash[path] = result
+            return result

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/interfaces.py
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/interfaces.py	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/interfaces.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,21 @@
+#############################################################################
+#
+# Copyright (c) 2006-2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+from zope.interface import Interface
+
+
+class IResourceContentsHash(Interface):
+
+    def __str__():
+        """return a hash of the contents of the resource"""

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/testing.py
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/testing.py	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/testing.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,62 @@
+#############################################################################
+#
+# Copyright (c) 2006-2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""tests
+
+$Id: test_directoryresource.py 95447 2009-01-29 16:28:18Z wosc $
+"""
+import os
+import re
+import tempfile
+import z3c.hashedresource.tests
+import zope.app.testing.functional
+import zope.publisher.browser
+import zope.security.checker
+
+
+fixture = os.path.join(
+    os.path.dirname(z3c.hashedresource.tests.__file__), 'fixture')
+
+checker = zope.security.checker.NamesChecker(
+    ('get', '__getitem__', 'request', 'publishTraverse')
+    )
+
+HashedResourcesLayer = zope.app.testing.functional.ZCMLLayer(
+    os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'),
+    __name__, 'HashedResourcesLayer', allow_teardown=True)
+
+
+class FunctionalTestCase(zope.app.testing.functional.FunctionalTestCase):
+
+    layer = HashedResourcesLayer
+
+    def assertMatches(self, regex, text):
+        self.assert_(re.match(regex, text), "/%s/ did not match '%s'" % (
+            regex, text))
+
+    def setUp(self):
+        super(FunctionalTestCase, self).setUp()
+        self.site = zope.site.hooks.getSite()
+
+        self.tmpdir = tempfile.mkdtemp()
+        open(os.path.join(self.tmpdir, 'example.txt'), 'w').write('')
+        self.dirname = os.path.basename(self.tmpdir)
+
+        self.request = zope.publisher.browser.TestRequest()
+        self.request._vh_root = self.site
+        self.directory = zope.app.publisher.browser.directoryresource.DirectoryResourceFactory(
+            self.tmpdir, checker, self.dirname)(self.request)
+        self.directory.__parent__ = self.site
+
+    def _hash(self, text):
+        return re.match('http://127.0.0.1/\+\+noop\+\+([^/]*)/.*', text).group(1)

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/tests/__init__.py
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/tests/__init__.py	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/tests/__init__.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1 @@
+# python package

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/tests/fixture/test.pt
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/tests/fixture/test.pt	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/tests/fixture/test.pt	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1 @@
+<html><body><p>test</p></body></html>

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/tests/fixture/test.txt
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/tests/fixture/test.txt	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/tests/fixture/test.txt	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,2 @@
+test
+data

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_browser.py
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_browser.py	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_browser.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,60 @@
+#############################################################################
+#
+# Copyright (c) 2006-2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""tests
+
+$Id: test_directoryresource.py 95447 2009-01-29 16:28:18Z wosc $
+"""
+from z3c.hashedresource import interfaces, testing
+import unittest
+import zope.app.testing.functional
+import zope.testbrowser.testing
+
+
+class BrowserTest(zope.app.testing.functional.FunctionalTestCase):
+
+    layer = testing.HashedResourcesLayer
+
+    def setUp(self):
+        super(BrowserTest, self).setUp()
+        self.browser = zope.testbrowser.testing.Browser()
+        self.directory = zope.component.getAdapter(
+            zope.publisher.browser.TestRequest(), name='myresource')
+
+    def test_traverse_atat_by_name(self):
+        self.browser.open('http://localhost/@@/myresource/test.txt')
+        self.assertEqual('test\ndata\n', self.browser.contents)
+
+    def test_traverse_atat_by_hash(self):
+        hash = str(
+            interfaces.IResourceContentsHash(self.directory))
+        self.browser.open(
+            'http://localhost/++noop++%s/@@/myresource/test.txt' % hash)
+        self.assertEqual('test\ndata\n', self.browser.contents)
+
+    def test_traverse_resource_by_name(self):
+        self.browser.open('http://localhost/++resource++myresource/test.txt')
+        self.assertEqual('test\ndata\n', self.browser.contents)
+
+    def test_traverse_resource_by_hash(self):
+        hash = str(
+            interfaces.IResourceContentsHash(self.directory))
+        self.browser.open(
+            'http://localhost/++noop++%s/++resource++myresource/test.txt' % hash)
+        self.assertEqual('test\ndata\n', self.browser.contents)
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(BrowserTest))
+    return suite

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_hash.py
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_hash.py	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_hash.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,39 @@
+#############################################################################
+#
+# Copyright (c) 2006-2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""tests
+
+$Id: test_directoryresource.py 95447 2009-01-29 16:28:18Z wosc $
+"""
+from z3c.hashedresource import hash, testing
+import unittest
+import zope.component
+
+
+class CachingContentsHashTest(testing.FunctionalTestCase):
+
+    def test_production_mode_hash_should_not_change(self):
+        zope.component.provideAdapter(
+            hash.CachingContentsHash,
+            (zope.app.publisher.browser.directoryresource.DirectoryResource,))
+
+        before = self._hash(self.directory())
+        open(os.path.join(self.tmpdir, 'example.txt'), 'w').write('foo')
+        after = self._hash(self.directory())
+        self.assertEqual(before, after)
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(CachingContentsHashTest))
+    return suite

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_url.py
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_url.py	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/tests/test_url.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,57 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""tests
+
+$Id: test_directoryresource.py 95447 2009-01-29 16:28:18Z wosc $
+"""
+from z3c.hashedresource import hash, interfaces, testing
+import os
+import unittest
+import zope.app.publisher.browser.directoryresource
+import zope.app.publisher.browser.tests
+import zope.app.publisher.testing
+import zope.app.testing.functional
+import zope.site.hooks
+
+
+class HashingURLTest(testing.FunctionalTestCase):
+
+    def test_directory_url_should_contain_hash(self):
+        self.assertMatches(
+            'http://127.0.0.1/\+\+noop\+\+[^/]*/@@/%s' % self.dirname, self.directory())
+
+    def test_file_url_should_contain_hash(self):
+        file = zope.app.publisher.browser.fileresource.FileResourceFactory(
+            os.path.join(testing.fixture, 'test.txt'), testing.checker, 'test.txt')(self.request)
+        self.assertMatches(
+            'http://127.0.0.1/\+\+noop\+\+[^/]*/@@/test.txt', file())
+
+    def test_different_files_hashes_should_differ(self):
+        file1 = zope.app.publisher.browser.fileresource.FileResourceFactory(
+            os.path.join(testing.fixture, 'test.txt'), testing.checker, 'test.txt')(self.request)
+        file2 = zope.app.publisher.browser.fileresource.FileResourceFactory(
+            os.path.join(testing.fixture, 'test.pt'), testing.checker, 'test.txt')(self.request)
+        self.assertNotEqual(self._hash(file1()), self._hash(file2()))
+
+    def test_directory_contents_changed_hash_should_change(self):
+        before = self._hash(self.directory())
+        open(os.path.join(self.tmpdir, 'example.txt'), 'w').write('foo')
+        after = self._hash(self.directory())
+        self.assertNotEqual(before, after)
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(HashingURLTest))
+    return suite

Added: z3c.hashedresource/trunk/src/z3c/hashedresource/url.py
===================================================================
--- z3c.hashedresource/trunk/src/z3c/hashedresource/url.py	                        (rev 0)
+++ z3c.hashedresource/trunk/src/z3c/hashedresource/url.py	2009-06-15 11:57:58 UTC (rev 100984)
@@ -0,0 +1,45 @@
+#############################################################################
+#
+# Copyright (c) 2006-2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+from z3c.hashedresource import interfaces
+from zope.component import adapts
+from zope.interface import implements, implementsOnly
+import zope.app.publisher.interfaces
+import zope.publisher.interfaces.browser
+import zope.traversing.browser.absoluteurl
+import zope.traversing.browser.interfaces
+
+
+class HashingURL(zope.traversing.browser.absoluteurl.AbsoluteURL):
+    """Inserts a hash of the contents into the resource's URL,
+    so the URL changes whenever the contents change, thereby forcing
+    a browser to update its cache.
+    """
+
+    implementsOnly(zope.traversing.browser.interfaces.IAbsoluteURL)
+    adapts(zope.app.publisher.interfaces.IResource,
+           zope.publisher.interfaces.browser.IDefaultBrowserLayer)
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+        # XXX duplicated from zope.app.publisher.browser.resource.AbsoluteURL
+        self.name = self.context.__name__
+        if self.name.startswith('++resource++'):
+            self.name = self.name[12:]
+
+    def __str__(self):
+        hash = str(interfaces.IResourceContentsHash(self.context))
+        return "%s/++noop++%s/@@/%s" % (self._site_url(), hash, self.name)



More information about the Checkins mailing list