[Checkins] SVN: lovely.responsecache/trunk/ - added a utility to
purge cache entries. a page to manually purge cache
Bernd Roessl
bernd.roessl at lovelysystems.com
Sat Jan 19 17:13:22 EST 2008
Log message for revision 82957:
- added a utility to purge cache entries. a page to manually purge cache
entries is also included
Changed:
U lovely.responsecache/trunk/CHANGES.txt
U lovely.responsecache/trunk/setup.py
U lovely.responsecache/trunk/src/lovely/responsecache/BROWSER.txt
A lovely.responsecache/trunk/src/lovely/responsecache/PURGE.txt
A lovely.responsecache/trunk/src/lovely/responsecache/PURGEVIEW.txt
U lovely.responsecache/trunk/src/lovely/responsecache/configure.zcml
U lovely.responsecache/trunk/src/lovely/responsecache/ftesting.zcml
U lovely.responsecache/trunk/src/lovely/responsecache/interfaces.py
U lovely.responsecache/trunk/src/lovely/responsecache/meta.zcml
A lovely.responsecache/trunk/src/lovely/responsecache/purge.py
U lovely.responsecache/trunk/src/lovely/responsecache/tests.py
U lovely.responsecache/trunk/src/lovely/responsecache/view.py
U lovely.responsecache/trunk/src/lovely/responsecache/zcml.py
U lovely.responsecache/trunk/src/lovely/responsecache/zcml.txt
-=-
Modified: lovely.responsecache/trunk/CHANGES.txt
===================================================================
--- lovely.responsecache/trunk/CHANGES.txt 2008-01-19 20:33:47 UTC (rev 82956)
+++ lovely.responsecache/trunk/CHANGES.txt 2008-01-19 22:13:22 UTC (rev 82957)
@@ -2,6 +2,12 @@
Changes for lovely.responsecache
================================
+2008/01/19 0.4.0
+================
+
+- added a utility to purge cache entries. a page to manually purge cache
+ entries is also included
+
2007/12/04 0.3.0a3
==================
Modified: lovely.responsecache/trunk/setup.py
===================================================================
--- lovely.responsecache/trunk/setup.py 2008-01-19 20:33:47 UTC (rev 82956)
+++ lovely.responsecache/trunk/setup.py 2008-01-19 22:13:22 UTC (rev 82957)
@@ -23,7 +23,7 @@
setup(
name = 'lovely.responsecache',
- version = '0.3.0a3',
+ version = '0.4.0',
author = "Lovely Systems",
author_email = "office at lovelysystems.com",
description = "Cache results of ContentProviders",
@@ -36,10 +36,12 @@
package_dir = {'':'src'},
namespace_packages = ['lovely',],
install_requires = ['setuptools',
+ 'pycurl',
'lovely.memcached',
'zope.app.publication',
'zope.app.security',
'zope.contentprovider',
+ 'zope.formlib',
'zope.publisher',
'zope.schema',
'zope.security',
Modified: lovely.responsecache/trunk/src/lovely/responsecache/BROWSER.txt
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/BROWSER.txt 2008-01-19 20:33:47 UTC (rev 82956)
+++ lovely.responsecache/trunk/src/lovely/responsecache/BROWSER.txt 2008-01-19 22:13:22 UTC (rev 82957)
@@ -75,7 +75,7 @@
/++ckey++authenticated/test.html
/++ckey++authenticated/test.html/IContent
/++ckey++authenticated/test.html/MyViewlet
-
+
We can also cascade such keys.
>>> browser.open('http://localhost/++ckey++anonymous/'
@@ -84,9 +84,10 @@
/++ckey++anonymous/++ckey++aSessionId/test.html
/++ckey++anonymous/++ckey++aSessionId/test.html/IContent
/++ckey++anonymous/++ckey++aSessionId/test.html/MyViewlet
-
+
>>> browser.open('http://localhost/++ckey++anonymous/'
... '++ckey++aSessionId/test.html')
>>> print browser.headers.get('x-memcached-hit')
/++ckey++anonymous/++ckey++aSessionId/test.html
-
+
+
Added: lovely.responsecache/trunk/src/lovely/responsecache/PURGE.txt
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/PURGE.txt (rev 0)
+++ lovely.responsecache/trunk/src/lovely/responsecache/PURGE.txt 2008-01-19 22:13:22 UTC (rev 82957)
@@ -0,0 +1,205 @@
+=========================
+Purging an external cache
+=========================
+
+ - multiple caches
+ - zmi UI to manually purge
+ - implemented as utility
+ - configured by zcml
+
+
+Let's create a purge utility. To ::
+
+ >>> from zope import component
+ >>> from lovely.responsecache.purge import PurgeUtil
+ >>> HTTP_PORT = 33334
+ >>> hosts = ['http://localhost:%d' % HTTP_PORT]
+ >>> util = PurgeUtil(hosts, timeout=1, retryDelay=0)
+ >>> component.provideUtility(util)
+
+Let's purge an expression::
+
+ >>> util.purge('http://domain/purge_expression1')
+
+Now call the endOfRequest function. NOTE: In wild this function is registed
+as an event handler and will be called at the end of any request::
+
+ >>> from lovely.responsecache.purge import endOfRequest
+ >>> log_info.clear()
+ >>> endOfRequest(None)
+ >>> print log_info
+ lovely.responsecache.purge ERROR
+ unable to purge 'http://localhost:33334/purge_expression1', reason: (7, "couldn't connect to host")
+
+As we can see we could not reach any server. So let's set up a http server::
+
+ >>> import threading
+ >>> from BaseHTTPServer import HTTPServer
+ >>> from SimpleHTTPServer import SimpleHTTPRequestHandler
+
+ >>> class StoppableHttpServer(HTTPServer):
+ ... """http server that reacts to self.stop flag"""
+ ... def serve_forever (self):
+ ... """Handle one request at a time until stopped."""
+ ... self.stop = False
+ ... while not self.stop:
+ ... self.handle_request()
+
+ >>> purgedUrls = []
+ >>> class StoppableHttpRequestHandler(SimpleHTTPRequestHandler):
+ ... """http request handler with QUIT stopping the server"""
+ ... def do_QUIT (self):
+ ... """send 200 OK response, and set server.stop to True"""
+ ... self.send_response(200)
+ ... self.end_headers()
+ ... self.server.stop = True
+ ... def do_PURGE (self):
+ ... """log the purge"""
+ ... purgedUrls.append(self.path)
+ ... self.send_response(200)
+ ... self.end_headers()
+
+ >>> def startServer():
+ ... address = ("localhost", HTTP_PORT)
+ ... server = StoppableHttpServer(address, StoppableHttpRequestHandler)
+ ... server.serve_forever()
+
+ >>> serverThread = threading.Thread(target=startServer)
+ >>> serverThread.start()
+
+OK, next try to purge some cache entries::
+
+ >>> log_info.clear()
+ >>> util.purge('http://domain/purge_expression1')
+ >>> endOfRequest(None)
+ >>> print log_info
+
+ >>> purgedUrls
+ ['/purge_expression1']
+
+If the endOfRequest get called twice there should not be more to purge::
+
+ >>> purgedUrls = []
+ >>> endOfRequest(None)
+ >>> purgedUrls
+ []
+
+Now call purge with more than one expressions::
+
+ >>> purgedUrls = []
+ >>> util.purge('http://domain/purge_expression1')
+ >>> util.purge('http://domain/purge_expression2')
+ >>> endOfRequest(None)
+ >>> purgedUrls
+ ['/purge_expression1', '/purge_expression2']
+
+Now call purge with duplicated expressions::
+
+ >>> purgedUrls = []
+ >>> util.purge('http://domain/purge_expression1')
+ >>> util.purge('http://domain/purge_expression2')
+ >>> util.purge('http://domain/purge_expression1')
+ >>> endOfRequest(None)
+ >>> purgedUrls
+ ['/purge_expression1', '/purge_expression2']
+
+If there are multiple hosts to purge it has to work this way::
+
+ >>> HTTP_PORT2 = 33335
+ >>> hosts = ['http://localhost:%d' % HTTP_PORT,
+ ... 'http://localhost:%d' % HTTP_PORT2]
+ >>> util.hosts = hosts
+
+ >>> def startServer2():
+ ... address = ("localhost", HTTP_PORT2)
+ ... server = StoppableHttpServer(address, StoppableHttpRequestHandler)
+ ... server.serve_forever()
+
+ >>> serverThread2 = threading.Thread(target=startServer2)
+ >>> serverThread2.start()
+
+ >>> purgedUrls = []
+ >>> util.purge('http://domain/purge_expression1')
+ >>> endOfRequest(None)
+ >>> purgedUrls
+ ['/purge_expression1', '/purge_expression1']
+
+If a server is not reachable it should be ignored for the configured
+retryDelay. Currently the retryDelay is set to zero so we have to set
+a proper one::
+
+ >>> util.retryDelay = 2
+
+ >>> from httplib import HTTPConnection
+ >>> conn = HTTPConnection("localhost:%d" % HTTP_PORT)
+ >>> conn.request("QUIT", "/")
+ >>> ret = conn.getresponse()
+
+ >>> log_info.clear()
+ >>> purgedUrls = []
+ >>> util.purge('http://domain/purge_expression1')
+ >>> endOfRequest(None)
+ >>> purgedUrls
+ ['/purge_expression1']
+
+As we can see there is just one purge in the list. So we have to checkout
+the logging info::
+
+ >>> print log_info
+ lovely.responsecache.purge ERROR
+ unable to purge 'http://localhost:33334/purge_expression1', reason: (7, "couldn't connect to host")
+
+The failed host is listed in the dict failedHosts::
+
+ >>> util.failedHosts
+ {'http://localhost:33334': ...}
+
+If we purge once again the host on the failedHosts should not get purged::
+
+ >>> log_info.clear()
+ >>> purgedUrls = []
+ >>> util.purge('http://domain/purge_expression1')
+ >>> endOfRequest(None)
+ >>> purgedUrls
+ ['/purge_expression1']
+
+ >>> print log_info
+
+If the host is up again it will be ignored till the rertyDelay elapsed::
+
+ >>> serverThread = threading.Thread(target=startServer)
+ >>> serverThread.start()
+
+ >>> log_info.clear()
+ >>> purgedUrls = []
+ >>> util.purge('http://domain/purge_expression1')
+ >>> endOfRequest(None)
+ >>> purgedUrls
+ ['/purge_expression1']
+
+ >>> print log_info
+
+Now lets wait until the retryDelay was elapsed and than the host will
+get purged again::
+
+ >>> from time import sleep
+ >>> sleep(2)
+
+ >>> log_info.clear()
+ >>> purgedUrls = []
+ >>> util.purge('http://domain/purge_expression1')
+ >>> endOfRequest(None)
+ >>> purgedUrls
+ ['/purge_expression1', '/purge_expression1']
+
+Stopping the http servers::
+
+ >>> from httplib import HTTPConnection
+ >>> conn = HTTPConnection("localhost:%d" % HTTP_PORT)
+ >>> conn.request("QUIT", "/")
+ >>> ret = conn.getresponse()
+
+ >>> from httplib import HTTPConnection
+ >>> conn = HTTPConnection("localhost:%d" % HTTP_PORT2)
+ >>> conn.request("QUIT", "/")
+ >>> ret = conn.getresponse()
Property changes on: lovely.responsecache/trunk/src/lovely/responsecache/PURGE.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: lovely.responsecache/trunk/src/lovely/responsecache/PURGEVIEW.txt
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/PURGEVIEW.txt (rev 0)
+++ lovely.responsecache/trunk/src/lovely/responsecache/PURGEVIEW.txt 2008-01-19 22:13:22 UTC (rev 82957)
@@ -0,0 +1,43 @@
+The purge view
+==============
+
+This page is to manually purge cache entries.
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+ >>> browser.addHeader('Authorization','Basic mgr:mgrpw')
+ >>> browser.handleErrors = False
+
+The page is available on the root of the zmi if a purge utility was configured::
+
+ >>> browser.open('http://localhost/@@contents.html')
+ >>> browser.getLink('purge').click()
+ Traceback (most recent call last):
+ ...
+ LinkNotFoundError
+
+So let's configure a utility and call the page again. Now the menu item
+will be available::
+
+ >>> from zope import component
+ >>> from lovely.responsecache.purge import PurgeUtil
+ >>> HTTP_PORT = 33333
+ >>> hosts = ['http://localhost:%d' % HTTP_PORT]
+ >>> util = PurgeUtil(hosts, timeout=1, retryDelay=0)
+ >>> component.provideUtility(util)
+
+ >>> browser.open('http://localhost/@@contents.html')
+ >>> browser.getLink('purge').click()
+
+The expression is a required field::
+
+ >>> browser.getControl(name="form.actions.purge").click()
+ >>> 'There were errors' in browser.contents
+ True
+
+If the expression was entered the caches get purged::
+
+ >>> browser.getControl(name="form.expression").value = ".*js"
+ >>> browser.getControl(name="form.actions.purge").click()
+ >>> 'There were errors' in browser.contents
+ False
Property changes on: lovely.responsecache/trunk/src/lovely/responsecache/PURGEVIEW.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: lovely.responsecache/trunk/src/lovely/responsecache/configure.zcml
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/configure.zcml 2008-01-19 20:33:47 UTC (rev 82956)
+++ lovely.responsecache/trunk/src/lovely/responsecache/configure.zcml 2008-01-19 22:13:22 UTC (rev 82957)
@@ -11,6 +11,8 @@
<subscriber handler=".event.setCache"/>
<subscriber handler=".event.setAuthInfoCookie"/>
+ <subscriber for="zope.app.publication.interfaces.IEndRequestEvent"
+ handler=".purge.endOfRequest"/>
<view
name="ckey" type="*"
@@ -18,4 +20,21 @@
factory=".namespace.ckey"
/>
+ <browser:page
+ name="purge.html"
+ for="zope.app.folder.interfaces.IRootFolder"
+ permission="zope.ManageContent"
+ class=".view.PurgeView"
+ />
+
+ <browser:menuItem
+ title="purge"
+ for="zope.app.folder.interfaces.IRootFolder"
+ menu="zmi_views"
+ action="purge.html"
+ permission="zope.ManageContent"
+ order="2000"
+ filter="python:modules['lovely.responsecache.view'].canPurge(context)"
+ />
+
</configure>
Modified: lovely.responsecache/trunk/src/lovely/responsecache/ftesting.zcml
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/ftesting.zcml 2008-01-19 20:33:47 UTC (rev 82956)
+++ lovely.responsecache/trunk/src/lovely/responsecache/ftesting.zcml 2008-01-19 22:13:22 UTC (rev 82957)
@@ -4,7 +4,7 @@
xmlns:meta="http://namespaces.zope.org/meta"
i18n_domain="zope">
-
+
<include package="zope.app.securitypolicy" file="meta.zcml" />
<include
@@ -17,7 +17,8 @@
/>
<include package="zope.viewlet" file="meta.zcml" />
-
+ <include package="lovely.responsecache" file="meta.zcml" />
+
<include package="zope.app.authentication" />
<securityPolicy
component="zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
@@ -29,49 +30,49 @@
<role id="zope.Manager" title="Site Manager" />
-
+
<principal
id="zope.manager"
title="Administrator"
login="mgr"
password="mgrpw" />
-
+
<grant
role="zope.Manager"
principal="zope.manager"
/>
-
+
<unauthenticatedPrincipal
id="zope.anybody"
title="Unauthenticated User" />
<unauthenticatedGroup
id="zope.Anybody"
- title="Unauthenticated Users"
+ title="Unauthenticated Users"
/>
-
+
<authenticatedGroup
id="zope.Authenticated"
- title="Authenticated Users"
+ title="Authenticated Users"
/>
-
+
<everybodyGroup
id="zope.Everybody"
- title="All Users"
+ title="All Users"
/>
<include package="zope.contentprovider"/>
+ <include package="zope.formlib"/>
<include package="zope.viewlet"/>
<include package="lovely.memcached" />
<include package="lovely.responsecache" />
<include package="lovely.responsecache.testing" />
-
<grant permission="zope.View"
principal="zope.Everybody"/>
-
-
+
+
<grantAll role="zope.Manager" />
-
+
</configure>
Modified: lovely.responsecache/trunk/src/lovely/responsecache/interfaces.py
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/interfaces.py 2008-01-19 20:33:47 UTC (rev 82956)
+++ lovely.responsecache/trunk/src/lovely/responsecache/interfaces.py 2008-01-19 22:13:22 UTC (rev 82957)
@@ -35,3 +35,16 @@
required=False,
default=[])
+
+class IPurge(interface.Interface):
+
+ def purge(expr, escapes='+-'):
+ """Method to purge the hosts with the given expression"""
+
+
+class IPurgeView(interface.Interface):
+ """Schema for the purge view"""
+
+ expression = schema.TextLine(title=u'expression',
+ description=u'Expression to purge',
+ required=True)
Modified: lovely.responsecache/trunk/src/lovely/responsecache/meta.zcml
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/meta.zcml 2008-01-19 20:33:47 UTC (rev 82956)
+++ lovely.responsecache/trunk/src/lovely/responsecache/meta.zcml 2008-01-19 22:13:22 UTC (rev 82957)
@@ -10,6 +10,12 @@
handler=".zcml.cacheSettingsDirective"
/>
+ <meta:directive
+ name="purge"
+ schema=".zcml.IPurgeDirective"
+ handler=".zcml.purgeDirective"
+ />
+
</meta:directives>
</configure>
Added: lovely.responsecache/trunk/src/lovely/responsecache/purge.py
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/purge.py (rev 0)
+++ lovely.responsecache/trunk/src/lovely/responsecache/purge.py 2008-01-19 22:13:22 UTC (rev 82957)
@@ -0,0 +1,107 @@
+##############################################################################
+#
+# Copyright (c) 2006-2007 Lovely Systems 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.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import logging
+import threading
+from time import time
+import urlparse
+
+from zope import component, interface
+from zope.schema.fieldproperty import FieldProperty
+
+import interfaces
+
+
+storage = threading.local()
+EXPRS_ATTR='varnish_purgeurls'
+
+log = logging.getLogger(__name__)
+
+class PurgeUtil(object):
+ """Utilty to purge mutliple caches"""
+ interface.implements(interfaces.IPurge)
+
+ failedHosts = {}
+
+ def __init__(self, hosts, timeout, retryDelay):
+ self.hosts = hosts
+ self.timeout = timeout
+ self.retryDelay = retryDelay
+
+ def purge(self, expr, escapes='+-'):
+ for esc in escapes:
+ expr = expr.replace(esc, '\\' + esc)
+ if not hasattr(storage, EXPRS_ATTR):
+ setattr(storage, EXPRS_ATTR, set([expr]))
+ else:
+ getattr(storage, EXPRS_ATTR).add(expr)
+
+ def doPurge(self):
+ exprs = getattr(storage, EXPRS_ATTR, None)
+ if exprs is None:
+ return
+ for host in self.hosts:
+ if host in self.failedHosts.keys():
+ if self.failedHosts[host] + self.retryDelay > time():
+ continue
+ else:
+ del self.failedHosts[host]
+ def urls(exprs):
+ for expr in exprs:
+ url = self._expr2URL(expr)
+ yield urlparse.urlunparse(urlparse.urlparse(host)[:2] + url)
+ if not self._purgeURLs(urls(exprs)):
+ self.failedHosts[host] = time()
+ delattr(storage, EXPRS_ATTR)
+
+ def _expr2URL(self, expr):
+ #URL: scheme://netloc/path;parameters?query#fragment
+ expr = str(expr)
+ if expr.startswith('http://'):
+ parts = urlparse.urlparse(expr)
+ parts = parts[2:]
+ else:
+ parts = (expr, '', '', '')
+ return parts
+
+ def _purgeURLs(self, urls):
+ import pycurl
+ try:
+ c = pycurl.Curl()
+ c.setopt(c.WRITEFUNCTION, self.ignoreWrite)
+ c.setopt(c.CUSTOMREQUEST,'PURGE')
+ c.setopt(c.TIMEOUT, self.timeout)
+ for url in urls:
+ c.setopt(c.URL, url)
+ c.perform()
+ c.close()
+ return True
+ except Exception, e:
+ log.error('unable to purge %r, reason: %s' % (url, e))
+ return False
+
+ def ignoreWrite(data):
+ pass
+
+def endOfRequest(event):
+ utils = component.getAllUtilitiesRegisteredFor(interfaces.IPurge)
+ for util in utils:
+ util.doPurge()
+
+
Property changes on: lovely.responsecache/trunk/src/lovely/responsecache/purge.py
___________________________________________________________________
Name: svn:keywords
+ Id
Modified: lovely.responsecache/trunk/src/lovely/responsecache/tests.py
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/tests.py 2008-01-19 20:33:47 UTC (rev 82956)
+++ lovely.responsecache/trunk/src/lovely/responsecache/tests.py 2008-01-19 22:13:22 UTC (rev 82957)
@@ -29,8 +29,11 @@
from z3c.testing import layer
from lovely.memcached.interfaces import IMemcachedClient
+from zope.testing.loggingsupport import InstalledHandler
+
from view import ResponseCacheSettings
+
class IMyView(interface.Interface):
pass
@@ -54,11 +57,18 @@
test.globs['root'] = root
test.globs['IMyView'] = IMyView
+ log_info = InstalledHandler('lovely.responsecache.purge')
+ test.globs['log_info'] = log_info
+
+
def tearDown(test):
setup.placefulTearDown()
def test_suite():
+ fsuite = functional.FunctionalDocFileSuite('PURGEVIEW.txt')
+ fsuite.layer=ResponseCacheLayer
+
level1Suites = (
DocFileSuite(
'zcml.txt', setUp=setUp, tearDown=tearDown,
@@ -68,7 +78,13 @@
'credentials.txt', setUp=setUp, tearDown=tearDown,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
),
+ DocFileSuite(
+ 'PURGE.txt', setUp=setUp, tearDown=tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ fsuite,
)
+
fsuite = functional.FunctionalDocFileSuite('BROWSER.txt')
fsuite.layer=ResponseCacheLayer
level2Suites = (
Modified: lovely.responsecache/trunk/src/lovely/responsecache/view.py
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/view.py 2008-01-19 20:33:47 UTC (rev 82956)
+++ lovely.responsecache/trunk/src/lovely/responsecache/view.py 2008-01-19 22:13:22 UTC (rev 82957)
@@ -16,15 +16,19 @@
"""
__docformat__ = "reStructuredText"
-from interfaces import IResponseCacheSettings
+import re
from zope import interface
from zope import component
+from zope.formlib import form
from zope.schema.fieldproperty import FieldProperty
-from lovely.memcached.interfaces import IMemcachedClient
from zope.traversing.browser.absoluteurl import absoluteURL
+from zope.publisher.browser import BrowserPage
from zope.app.component.hooks import getSite
-import re
+from lovely.memcached.interfaces import IMemcachedClient
+from interfaces import IResponseCacheSettings, IPurge, IPurgeView
+
+
CKEY_PAT = pat = re.compile(r'\+\+ckey\+\+([^\/]*)\/')
class ResponseCacheSettings(object):
@@ -62,3 +66,16 @@
name=self.cacheName,
context=self.context)
+
+class PurgeView(form.AddForm):
+
+ form_fields = form.FormFields(IPurgeView)
+
+ @form.action(u'Purge')
+ def handle_purge_action(self, action, data):
+ util = component.getUtility(IPurge)
+ util.purge(data['expression'])
+
+
+def canPurge(context):
+ return component.queryUtility(IPurge) is not None
Modified: lovely.responsecache/trunk/src/lovely/responsecache/zcml.py
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/zcml.py 2008-01-19 20:33:47 UTC (rev 82956)
+++ lovely.responsecache/trunk/src/lovely/responsecache/zcml.py 2008-01-19 22:13:22 UTC (rev 82957)
@@ -27,9 +27,11 @@
from zope.proxy import removeAllProxies
from zope.configuration.fields import GlobalObject, Tokens
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+from zope.security.zcml import Permission
-from interfaces import IResponseCacheSettings
+from interfaces import IResponseCacheSettings, IPurge
from view import ResponseCacheSettings
+from purge import PurgeUtil
from zope.i18nmessageid import MessageFactory
_ = MessageFactory('lovely.responseheader')
@@ -193,3 +195,46 @@
_context.info),
)
+
+
+class IPurgeDirective(interface.Interface):
+ """Parameters for the purge directive."""
+
+ hosts = Tokens(
+ title=u'Hosts',
+ description=u'Lists of hosts to get purged',
+ required=True,
+ value_type=schema.URI(title=u"host"))
+
+ timeout = schema.Int(
+ title = _(u'Timeout'),
+ description=u'Timeout for purge requests in seconds. Keep it short!',
+ required=True,
+ default=1,
+ )
+
+ retryDelay = schema.Int(
+ title = _(u'Cachename'),
+ description=u'Retry delay to purge after a timeout in seconds.',
+ required=True,
+ default=60,
+ )
+
+ permission = Permission(
+ title=_("Permission"),
+ description=_("Permission required to use this component."),
+ required=False,
+ )
+
+
+def purgeDirective(_context, hosts, timeout, retryDelay, permission=None):
+ """Function to create a perge utility"""
+
+ util = PurgeUtil(hosts, timeout, retryDelay)
+ zcml.utility(_context,
+ provides=IPurge,
+ component=util,
+ permission=permission,
+ name='')
+
+
Modified: lovely.responsecache/trunk/src/lovely/responsecache/zcml.txt
===================================================================
--- lovely.responsecache/trunk/src/lovely/responsecache/zcml.txt 2008-01-19 20:33:47 UTC (rev 82956)
+++ lovely.responsecache/trunk/src/lovely/responsecache/zcml.txt 2008-01-19 22:13:22 UTC (rev 82957)
@@ -216,3 +216,24 @@
>>> settings.someOtherVariable
u'B'
+Purge
+=====
+
+Configure a purge utility and check if it was created::
+
+ >>> xmlconfig(StringIO(baseTemplate% (
+ ... """
+ ... <purge
+ ... hosts="http://localhost http://otherhost"
+ ... />
+ ... """
+ ... )))
+
+ >>> from lovely.responsecache.interfaces import IPurge
+ >>> purger = component.getUtility(IPurge)
+ >>> purger
+ <lovely.responsecache.purge.PurgeUtil object at ...>
+
+ >>> purger.hosts
+ ['http://localhost', 'http://otherhost']
+
More information about the Checkins
mailing list