{ "info": { "author": "Stephan Richter and the Zope Community", "author_email": "zope3-dev@zope.org", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Environment :: Web Environment", "Framework :: Zope3", "Intended Audience :: Developers", "License :: OSI Approved :: Zope Public License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Internet :: WWW/HTTP" ], "description": "Versioned Resources insert a version number in the URL of a resource, so that\ncache behavior can be customized.\n\n\nDetailed Documentation\n**********************\n\n===================\nVersioned Resources\n===================\n\nWhen deploying scalable Web applications, it is important to serve all static\nresources such as Javascript code, CSS, and images as quickly as possible\nusing as little resources as possible. On the hand, the resources must be\nknown within the application, so that they can be properly referenced.\n\nAdditionally, we want to set the expiration date as far in the future as\npossible. However, sometimes we need or want to update a resource before the\nexpiration date has arrived. Yahoo has solved this problem by including a\nversion number into the URL, which then allowed them to set the expiration\ndate effectively to infinity.\n\nHowever, maintaining the versions manually is a nightmare, since you would not\nonly need to change file or directory names all the time as you change them,\nbut also adjust your code. This package aims to solve this problem by\nproviding a central component to manage the versions of the resources and make\nversioning transparent to the developer.\n\n\nThe Version Manager\n-------------------\n\nThe Version Manager is a central component that provides the version to the\nsystem. It is up to the version manager to decide whether the version is for\nexample based on a tag, a revision number, the package version number or a\nmanually entered version.\n\n >>> from z3c.versionedresource import version\n\nThis package provides only a simple version manager, since I have found no\nother good version indicator that is available generically.\n\n >>> manager = version.VersionManager('1.0.0')\n >>> manager\n \n\nThe only constructor argument is the version itself. Versions must be ASCII\nstrings. Let's now register the version manager, so that it is available for\nlater use:\n\n >>> import zope.component\n >>> zope.component.provideUtility(manager)\n\nClearly, there is not much to version managers and they are only interesting\nwithin the larger context of this package.\n\nAn advanced implementation of the version manager uses the SVN revision number\nto produce the version string. You simply pass in a working path and the\nversion is computed:\n\n >>> import os\n >>> manager = version.SVNVersionManager(os.path.dirname(__file__))\n >>> manager\n \n\n\nVersioned Resource Traversal\n----------------------------\n\nZope uses a special, empty-named view to traverse resources from a site like\nthis::\n\n /@@/\n\nWe would like to support URLs like this now::\n\n /@@//\n\nThat means that we need a custom implementation of the resources view that can\nhandle the version.\n\n >>> from zope.publisher.browser import TestRequest\n >>> from z3c.versionedresource import resource\n\n >>> request = TestRequest()\n >>> context = object()\n\n >>> resources = resource.Resources(context, request)\n\nThe resources object is a browser view:\n\n >>> resources.__parent__ is context\n True\n >>> resources.__name__\n\nThe view is also a browser publisher. But it does not support a default:\n\n >>> resources.browserDefault(request)\n (, ())\n\nWhen traversing to a sub-item, the version can be specified:\n\n >>> resources.publishTraverse(request, '1.0.0')\n \n\nThe result of the traversal is the original resources object. When asking for\nan unknown resource or version, a ``NotFound`` is raised:\n\n >>> resources.publishTraverse(request, 'error')\n Traceback (most recent call last):\n ...\n NotFound: Object: , name: 'error'\n\nLet's now register a resource, just to show that traversing to it works:\n\n >>> import zope.interface\n >>> from zope.app.publisher.browser.resource import Resource\n\n >>> zope.component.provideAdapter(\n ... Resource, (TestRequest,), zope.interface.Interface, 'resource.css')\n\nWe can now ask the resources object to traverse the resource:\n\n >>> css = resources.publishTraverse(request, 'resource.css')\n >>> css\n \n\nCalling it will return the URL:\n\n >>> css()\n 'http://127.0.0.1/@@/resource.css'\n\nMmmh, so a regular resource does not honor the version element. That's because\nit's ``__call__()`` method defines how the URL is constructed, which is\nignorant of the version.\n\nSo let's use this package's implementation of a resource:\n\n >>> zope.component.provideAdapter(\n ... resource.Resource,\n ... (TestRequest,), zope.interface.Interface, 'resource2.css')\n\n >>> css = resources.publishTraverse(request, 'resource2.css')\n >>> css\n \n >>> css()\n 'http://127.0.0.1/@@/1.0.0/resource2.css'\n\n\nCustom Resource Classes\n-----------------------\n\nThe ``zope.app.publisher.browser`` package defines three major\nresources. As pointed out above, they have to be adjusted to produce a\nversioned URL and set the cache header to a long time.\n\nFile Resource\n~~~~~~~~~~~~~\n\nThe versioned file resource is identical to the default file resource, except\nthat it has a versioned URL and a 10 year cache timeout.\n\n >>> import os.path\n >>> import z3c.versionedresource.tests\n >>> filesdir = os.path.join(\n ... os.path.dirname(z3c.versionedresource.tests.__file__),\n ... 'testfiles')\n\n >>> from zope.app.publisher.fileresource import File\n >>> file = File(os.path.join(filesdir, 'test.txt'), 'test.txt')\n >>> request = TestRequest()\n >>> res = resource.FileResource(file, request)\n >>> res.__name__ = 'ajax.js'\n >>> res\n \n >>> res.cacheTimeout\n 315360000\n >>> res()\n 'http://127.0.0.1/@@/1.0.0/ajax.js'\n\nTwo factories, one for files and one for images is used:\n\n >>> factory = resource.FileResourceFactory(\n ... os.path.join(filesdir, 'test.txt'), None, 'test.txt')\n >>> factory\n \n >>> factory(request)\n \n\n >>> factory = resource.ImageResourceFactory(\n ... os.path.join(filesdir, 'test.gif'), None, 'test.gif')\n >>> factory\n \n >>> factory(request)\n \n\n\nDirectory Resource\n~~~~~~~~~~~~~~~~~~\n\nLet's now turn to directories. The trick here is that we need to set all the\nfactories correctly. So let's create the resource first:\n\n >>> from zope.app.publisher.browser.directoryresource import Directory\n\n >>> request = TestRequest()\n >>> res = resource.DirectoryResource(\n ... Directory(os.path.join(filesdir, 'subdir'), None, 'subdir'), request)\n >>> res.__name__ = 'subdir'\n >>> res\n \n >>> res()\n 'http://127.0.0.1/@@/1.0.0/subdir'\n\nLet's try to traverse to some files in the directory:\n\n >>> res.publishTraverse(request, 'test.gif')\n \n\nWe also have a factory for it:\n\n >>> factory = resource.DirectoryResourceFactory(\n ... os.path.join(filesdir, 'subdir'), None, 'subdir')\n >>> factory\n \n >>> factory(request)\n \n\n\nCustom ZCML Directives\n----------------------\n\nTo make the new resources easily usable, we also need custom resource\ndirectives:\n\n >>> from zope.configuration import xmlconfig\n >>> import z3c.versionedresource\n >>> context = xmlconfig.file('meta.zcml', z3c.versionedresource)\n\nLet's register simple versioned resource:\n\n >>> context = xmlconfig.string(\"\"\"\n ... \n ... \n ... \n ... \"\"\" %os.path.join(filesdir, 'test.gif') , context=context)\n\nNow we can access the resource:\n\n >>> resources.publishTraverse(request, '1.0.0')\\\n ... .publishTraverse(request, 'zcml-test.gif')()\n 'http://127.0.0.1/@@/1.0.0/zcml-test.gif'\n\nYou can also specify a simple file resource,\n\n >>> context = xmlconfig.string(\"\"\"\n ... \n ... \n ... \n ... \"\"\" %os.path.join(filesdir, 'test.txt') , context=context)\n\n >>> resources.publishTraverse(request, '1.0.0')\\\n ... .publishTraverse(request, 'zcml-test.txt').context\n \n\n >>> unregister('zcml-test.txt')\n\nas well as a page template.\n\n >>> context = xmlconfig.string(\"\"\"\n ... \n ... \n ... \n ... \"\"\" %os.path.join(filesdir, 'test.pt') , context=context)\n\n >>> resources.publishTraverse(request, '1.0.0')\\\n ... .publishTraverse(request, 'zcml-test.html').context\n \n\nNote that the page template resource cannot be a versioned resource, since it\nhas dynamic components:\n\n >>> resources.publishTraverse(request, '1.0.0')\\\n ... .publishTraverse(request, 'zcml-test.html')()\n u'

Test

\\n'\n\nNote: Eeek, `zope.app.publisher.browser` is broken here. We should have\ngotten a URL back.\n\n >>> unregister('zcml-test.html')\n\nFinally, a factory can also be passed in:\n\n >>> context = xmlconfig.string(\"\"\"\n ... \n ... \n ... \n ... \"\"\", context=context)\n\n >>> resources.publishTraverse(request, '1.0.0')\\\n ... .publishTraverse(request, 'zcml-dyn.html')\n \n\n >>> unregister('zcml-dyn.html')\n\nLet's now create a directory resource:\n\n >>> context = xmlconfig.string(\"\"\"\n ... \n ... \n ... \n ... \"\"\" %os.path.join(filesdir, 'subdir') , context=context)\n\nAnd access it:\n\n >>> resources.publishTraverse(request, '1.0.0')\\\n ... .publishTraverse(request, 'zcml-subdir')()\n 'http://127.0.0.1/@@/1.0.0/zcml-subdir'\n\nThe directives also have some error handling built-in. So let's have a\nlook. In the ``browser:versionedResource`` directive, you can only specify\neither a template, file, image or factory:\n\n >>> context = xmlconfig.string(\"\"\"\n ... \n ... \n ... \n ... \"\"\", context=context)\n Traceback (most recent call last):\n ...\n ZopeXMLConfigurationError: File \"\", line 4.2-8.8\n ConfigurationError: Must use exactly one of factory or file or\n image or template attributes for resource directives\n\nThe resource directive on the other hand, ensures that the specified path is a\ndirectory:\n\n >>> context = xmlconfig.string(\"\"\"\n ... \n ... \n ... \n ... \"\"\", context=context)\n Traceback (most recent call last):\n ...\n ZopeXMLConfigurationError: File \"\", line 4.2-7.8\n ConfigurationError: Directory /foo does not exist\n\n\nListing All Resources\n---------------------\n\nFinally, there exists a script that will list all resources registered as\nversioned resources with the system.\n\n >>> from z3c.versionedresource import list\n\n >>> list.produceResources(list.get_options(\n ... ['-u', 'http://zope.org',\n ... '-l', 'z3c.versionedresource.tests.test_doc.ITestLayer',\n ... '--list-only',\n ... os.path.join(os.path.dirname(list.__file__), 'tests', 'simple.zcml')]\n ... ))\n http://zope.org/@@/1.0.0/zcml-subdir/test.gif\n http://zope.org/@@/1.0.0/zcml-subdir/subsubdir/subtest.gif\n http://zope.org/@@/1.0.0/zcml-test.gif\n\nYou can also produce the resources in a directory:\n\n >>> import tempfile\n >>> outdir = tempfile.mkdtemp()\n\n >>> list.produceResources(list.get_options(\n ... ['-u', 'http://zope.org',\n ... '-l', 'z3c.versionedresource.tests.test_doc.ITestLayer',\n ... '-d', outdir,\n ... os.path.join(os.path.dirname(list.__file__), 'tests', 'simple.zcml')]\n ... ))\n /.../1.0.0\n\n >>> ls(outdir)\n d 1.0.0 4096\n >>> ls(os.path.join(outdir, '1.0.0'))\n d zcml-subdir 4096\n f zcml-test.gif 909\n >>> ls(os.path.join(outdir, '1.0.0', 'zcml-subdir'))\n d subsubdir 4096\n f test.gif 909\n >>> ls(os.path.join(outdir, '1.0.0', 'zcml-subdir', 'subsubdir'))\n f subtest.gif 909\n\nThe module consists of several small helper functions, so let's look at them\nto verify their correct behavior.\n\n\n`getResources(layerPath, url='http://localhost/')` Function\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis function retrieves all versioned resources from the system for a given\nlayer. Optionally a URL can be passed in to alter the resource URLs.\n\n >>> resources = list.getResources(\n ... ['z3c.versionedresource.tests.test_doc.ITestLayer',\n ... 'z3c.versionedresource.tests.test_doc.ITestLayer2'])\n >>> sorted(resources)\n [(u'zcml-subdir', ),\n (u'zcml-test.gif', )]\n\nAs you can see, this list only provides the first layer. It is the\nresponsibility of the consuming code to digg deeper into the directory\nresources.\n\n\n`getResourceUrls(resources)` Function\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nOnce we have the list of resources, we can produce a full list of all\navailable paths.\n\n >>> sorted(list.getResourceUrls(resources))\n [u'http://localhost/@@/1.0.0/zcml-subdir/subsubdir/subtest.gif',\n u'http://localhost/@@/1.0.0/zcml-subdir/test.gif',\n u'http://localhost/@@/1.0.0/zcml-test.gif']\n\n\n`storeResource(dir, name, resource, zip=False)` Function\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe more interesting use case, however, is this function, which stores the\nresources in a directory. First we need to create an output directory:\n\n >>> outdir = tempfile.mkdtemp()\n\nWe can now store a resource to it:\n\n >>> list.storeResource(outdir, resources[1][0], resources[1][1])\n >>> ls(outdir)\n f zcml-test.gif 909\n\nLet's now zip it:\n\n >>> list.storeResource(outdir, resources[1][0], resources[1][1], True)\n >>> ls(outdir)\n f zcml-test.gif 252\n\nWhen storing a directory resource, all sub-items are stored as well:\n\n >>> list.storeResource(outdir, resources[0][0], resources[0][1], True)\n >>> ls(outdir)\n d zcml-subdir 4096\n f zcml-test.gif 252\n\n >>> ls(os.path.join(outdir, 'zcml-subdir'))\n d subsubdir 4096\n f test.gif 259\n\n >>> ls(os.path.join(outdir, 'zcml-subdir', 'subsubdir'))\n f subtest.gif 272\n\n\nSome odds and ends\n~~~~~~~~~~~~~~~~~~\n\nLet's use the `main()` function too. It is the one used by the script, but\nalways raises a system exist:\n\n >>> list.main()\n Traceback (most recent call last):\n ...\n SystemExit: 2\n\n >>> list.main(['foo'])\n Traceback (most recent call last):\n ...\n SystemExit: 1\n\n >>> list.main(\n ... ['-l', 'z3c.versionedresource.tests.test_doc.ITestLayer',\n ... '--list-only',\n ... os.path.join(os.path.dirname(list.__file__), 'tests', 'simple.zcml')]\n ... )\n Traceback (most recent call last):\n ...\n SystemExit: 0\n\nIf the positional argument is missing, then we get a parser error:\n\n >>> list.main(\n ... ['-l', 'z3c.versionedresource.tests.test_doc.ITestLayer',\n ... '--list-only'])\n Traceback (most recent call last):\n ...\n SystemExit: 2\n\n\n=======\nCHANGES\n=======\n\nVersion 0.5.0 (2009-07-30)\n--------------------------\n\n* Feature: Implemented SVN Version Manager.\n\n* Feature: Updated to work with latest package versions.\n\n* Bug: Fix registering of ``Resources`` and move ``configure.zcml`` to\n ``overrides.zcml``, because it overrides the ``zope.app.publisher``'s\n registration.\n\nVersion 0.4.0 (2008-09-27)\n--------------------------\n\n* Feature: Add ability to specify multiple layers.\n\nVersion 0.3.0 (2008-08-09)\n--------------------------\n\n* Feature: Do not fail, if a directory already exists.\n\n* Feature: Print an error before exiting with exit code 1.\n\n* Feature: Files in a directory ending in *.html are now converted to regular\n file resources and not as page template resources.\n\nVersion 0.2.0 (2008-08-09)\n--------------------------\n\n- Feature: List script can now also create directory with all resources in\n it. There is also an option to store the resources in gzip format.\n\n- Bug: Fixed test coverage to 100%.\n\nVersion 0.1.0 (2008-08-04)\n--------------------------\n\n- Initial Release\n\n * Version Manager manages the current version of resources.\n\n * Implemented custom resources and their directives.\n\n * Provided script to extract all versioned resources and produces their URLs.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://pypi.python.org/pypi/z3c.versionedresource", "keywords": "zope3 resource version", "license": "ZPL 2.1", "maintainer": null, "maintainer_email": null, "name": "z3c.versionedresource", "package_url": "https://pypi.org/project/z3c.versionedresource/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/z3c.versionedresource/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://pypi.python.org/pypi/z3c.versionedresource" }, "release_url": "https://pypi.org/project/z3c.versionedresource/0.5.0/", "requires_dist": null, "requires_python": null, "summary": "Versioned Resources", "version": "0.5.0" }, "last_serial": 802123, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "d8a2f4a7b1a059ebc0b29c9fc0812a6b", "sha256": "50fb87d61162d6be5c4365c52fcb91e3ecb5670201be7b19a79261f07bb536c4" }, "downloads": -1, "filename": "z3c.versionedresource-0.1.0.tar.gz", "has_sig": false, "md5_digest": "d8a2f4a7b1a059ebc0b29c9fc0812a6b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11586, "upload_time": "2008-08-05T21:15:36", "url": "https://files.pythonhosted.org/packages/1b/7a/3d577b88968d11b0929c85cbd224c87eeb1cae09e0ca202507a94145cd03/z3c.versionedresource-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "326a667e4d2cf070d083203748b51716", "sha256": "1fd3ed363b0efe1d69c19b8e02ae821874f4a3c56d0159550eedb2877e7b65b3" }, "downloads": -1, "filename": "z3c.versionedresource-0.2.0.tar.gz", "has_sig": false, "md5_digest": "326a667e4d2cf070d083203748b51716", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19953, "upload_time": "2008-08-10T06:09:57", "url": "https://files.pythonhosted.org/packages/a8/6f/bf49c7a10e99d7e53976b4d2e6e90e9597ad5e14c3701a9b9f906692596d/z3c.versionedresource-0.2.0.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "f8bdf7b791546f0963a8aa34a69ce7b3", "sha256": "935729ad70985580f6ae809ced58509bc79fc2241689631ca4359194c7417ea3" }, "downloads": -1, "filename": "z3c.versionedresource-0.3.0.tar.gz", "has_sig": false, "md5_digest": "f8bdf7b791546f0963a8aa34a69ce7b3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20118, "upload_time": "2008-08-10T07:32:29", "url": "https://files.pythonhosted.org/packages/5a/1f/db7f2c66e7806fcc567069fd6e4a87c0dbdb520281c8aaff0518cd1d41c1/z3c.versionedresource-0.3.0.tar.gz" } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "e9a933d2f2e2387986b417720a688d0f", "sha256": "6591aa4c6b1b0d02d7d32425b15fc11e0aaec799cf6986351cdaaeeacebcd3d1" }, "downloads": -1, "filename": "z3c.versionedresource-0.4.0.tar.gz", "has_sig": false, "md5_digest": "e9a933d2f2e2387986b417720a688d0f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20308, "upload_time": "2008-09-28T05:54:06", "url": "https://files.pythonhosted.org/packages/eb/88/61ac8aa63304cfd7dd66db214629016e15efd8a865cfc85846cb39389f50/z3c.versionedresource-0.4.0.tar.gz" } ], "0.5.0": [ { "comment_text": "", "digests": { "md5": "297468cab643586b6efb9c53fc66c05c", "sha256": "c9d1dc5054fb0f00b7bc71a424f4118dc1c30cb5052a476a1b80e4016239a475" }, "downloads": -1, "filename": "z3c.versionedresource-0.5.0.tar.gz", "has_sig": false, "md5_digest": "297468cab643586b6efb9c53fc66c05c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19235, "upload_time": "2009-07-30T18:24:31", "url": "https://files.pythonhosted.org/packages/06/3b/770a3bee8ff90314ccdaf9697b7766a960ecae5e660936ec9ec957fab284/z3c.versionedresource-0.5.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "297468cab643586b6efb9c53fc66c05c", "sha256": "c9d1dc5054fb0f00b7bc71a424f4118dc1c30cb5052a476a1b80e4016239a475" }, "downloads": -1, "filename": "z3c.versionedresource-0.5.0.tar.gz", "has_sig": false, "md5_digest": "297468cab643586b6efb9c53fc66c05c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19235, "upload_time": "2009-07-30T18:24:31", "url": "https://files.pythonhosted.org/packages/06/3b/770a3bee8ff90314ccdaf9697b7766a960ecae5e660936ec9ec957fab284/z3c.versionedresource-0.5.0.tar.gz" } ] }