[Checkins] SVN: z3c.caching/trunk/ Add the ability to explicitly declare types. Mainly for UI support, but if you set explicit=True, it can also be used to throw an error if undeclared types are used.
Martin Aspeli
optilude at gmx.net
Sat Jan 2 04:59:02 EST 2010
Log message for revision 107534:
Add the ability to explicitly declare types. Mainly for UI support, but if you set explicit=True, it can also be used to throw an error if undeclared types are used.
Changed:
U z3c.caching/trunk/README.txt
U z3c.caching/trunk/docs/HISTORY.txt
U z3c.caching/trunk/src/z3c/caching/interfaces.py
U z3c.caching/trunk/src/z3c/caching/meta.zcml
U z3c.caching/trunk/src/z3c/caching/registry.py
A z3c.caching/trunk/src/z3c/caching/tests/test3.zcml
A z3c.caching/trunk/src/z3c/caching/tests/test4.zcml
A z3c.caching/trunk/src/z3c/caching/tests/test5.zcml
U z3c.caching/trunk/src/z3c/caching/tests/test_registry.py
U z3c.caching/trunk/src/z3c/caching/tests/test_zcml.py
U z3c.caching/trunk/src/z3c/caching/zcml.py
-=-
Modified: z3c.caching/trunk/README.txt
===================================================================
--- z3c.caching/trunk/README.txt 2010-01-02 09:37:54 UTC (rev 107533)
+++ z3c.caching/trunk/README.txt 2010-01-02 09:59:02 UTC (rev 107534)
@@ -36,7 +36,13 @@
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:cache="http://namespaces.zope.org/cache"/>
-
+
+ <cache:rulesetType
+ name="plone-content-types"
+ title="Plone content types"
+ description="Non-folderish content types"
+ />
+
<cache:ruleset
for=".frontpage.FrontpageView"
ruleset="plone-content-types"
@@ -58,6 +64,10 @@
with an adapter registration, a more specific registration can be used to
override a more generic one.
+Above, we also add some metadata about the type of ruleset using the
+``<cache:rulesetType />`` directive. This is principally useful for UI support
+and can be often be skipped.
+
If you prefer to use python directly you can do so::
from z3c.caching.registry import register
@@ -65,11 +75,39 @@
register(FrontpageView, "plone-content-types")
-To find the ruleset for an object use the ``lookup`` method::
+To find the ruleset for an object use the ``lookup()`` method::
from z3c.caching.registry import lookup
cacheRule = lookup(FrontpageView)
+To declare the ruleset type metadata, use the ``declareType`` method::
+
+ from z3c.caching.registry import declareType
+ declareType = declareType(name="plone-content-types", \
+ title=u"Plone content types", \
+ description=u"Non-folderish content types")
+
+If you want to get a list of all declared types, use the ``enumerateTypes()``
+method::
+
+ from z3c.caching.registry import enumerate
+ for type_ in enumerateTypes():
+ ...
+
+The ``type_`` object provides ``IRulesetType`` and has attributes for
+``name``, ``title`` and ``description``.
+
+Strict mode
+-----------
+
+By default, you are not required to declare the type of a ruleset before using
+it. This is convenient, but increases the risk of typos or a proliferation of
+rulesets that are semantically equivalent. If you want to guard against this
+case, you can put the ruleset into explicit mode, like this::
+
+ from z3c.caching.registry import setExplicitMode
+ setExplicitMode(True)
+
Last modified date/time
-----------------------
Modified: z3c.caching/trunk/docs/HISTORY.txt
===================================================================
--- z3c.caching/trunk/docs/HISTORY.txt 2010-01-02 09:37:54 UTC (rev 107533)
+++ z3c.caching/trunk/docs/HISTORY.txt 2010-01-02 09:59:02 UTC (rev 107534)
@@ -4,11 +4,15 @@
2.0a1 - Unreleased
------------------
+* Added concept of an explicitly declare ruleset type. Optional by default,
+ but can be made required by setting `explicit` to `True`.
+ [optilude]
+
* Added ``ILastModified`` implementation for a view which delegates to the
view's context.
[optilude]
-* Added ``enumerate()`` method to the registry, used to list all currently
+* Added ``enumerateTypes()`` method to the registry, used to list all currently
used cache rule ids.
[optilude]
Modified: z3c.caching/trunk/src/z3c/caching/interfaces.py
===================================================================
--- z3c.caching/trunk/src/z3c/caching/interfaces.py 2010-01-02 09:37:54 UTC (rev 107533)
+++ z3c.caching/trunk/src/z3c/caching/interfaces.py 2010-01-02 09:59:02 UTC (rev 107534)
@@ -1,10 +1,11 @@
from zope.interface import Interface
+from zope import schema
class IRulesetRegistry(Interface):
def register(obj, rule):
"""Mark objects that are implementers of `obj` to use the caching
- rule `rule`. The value for `rule` MUST be a valid python identifier.
+ rule `rule`.
"""
def unregister(obj):
@@ -22,10 +23,36 @@
been registered `None` is returned.
"""
- def enumerate():
- """Return a sequence of all unique registered rule set ids (strings)
+ def __getitem__(obj):
+ """Convenience spelling for `lookup(obj)`.
"""
+
+ def declareType(name, type, description):
+ """Declare a new ruleset type. This will put a new `IRulesetType`
+ into the list of objects returned by `enumerate`.
+ """
+
+ def enumerateTypes():
+ """Return a sequence of all unique registered rule set types, as
+ ``IRuleSetType`` objects.
+ """
+
+ explicit = schema.Bool(
+ title=u"Explicit mode",
+ description=u"If true, ruleset types must be declared before being used.",
+ required=True,
+ default=False
+ )
+class IRulesetType(Interface):
+ """A ruleset type. The name can be used in a <cache:ruleset /> directive.
+ The title and description are used for UI support.
+ """
+
+ name = schema.ASCIILine(title=u"Ruleset name")
+ title = schema.TextLine(title=u"Title")
+ description = schema.TextLine(title=u"Description", required=False)
+
class ILastModified(Interface):
"""An abstraction to help obtain a last-modified date for a published
resource.
Modified: z3c.caching/trunk/src/z3c/caching/meta.zcml
===================================================================
--- z3c.caching/trunk/src/z3c/caching/meta.zcml 2010-01-02 09:37:54 UTC (rev 107533)
+++ z3c.caching/trunk/src/z3c/caching/meta.zcml 2010-01-02 09:59:02 UTC (rev 107534)
@@ -5,6 +5,12 @@
<meta:directives namespace="http://namespaces.zope.org/cache">
<meta:directive
+ name="rulesetType"
+ schema=".interfaces.IRulesetType"
+ handler=".zcml.rulesetType"
+ />
+
+ <meta:directive
name="ruleset"
schema=".zcml.IRuleset"
handler=".zcml.ruleset"
Modified: z3c.caching/trunk/src/z3c/caching/registry.py
===================================================================
--- z3c.caching/trunk/src/z3c/caching/registry.py 2010-01-02 09:37:54 UTC (rev 107533)
+++ z3c.caching/trunk/src/z3c/caching/registry.py 2010-01-02 09:59:02 UTC (rev 107534)
@@ -12,10 +12,11 @@
from zope.interface import implements, Interface, Attribute
-from zope.component import adapts, getGlobalSiteManager
+from zope.component import adapts, queryUtility, getUtilitiesFor, getGlobalSiteManager
from zope.component.interfaces import IComponents
from z3c.caching.interfaces import IRulesetRegistry
+from z3c.caching.interfaces import IRulesetType
class ICacheRule(Interface):
"""Represents the cache rule applied to an object.
@@ -31,6 +32,15 @@
def __init__(self, identifier):
self.id = identifier
+class RulesetType(object):
+ __slots__ = ('name', 'title', 'description',)
+ implements(IRulesetType)
+
+ def __init__(self, name, title, description):
+ self.name = name
+ self.title = title
+ self.description = description
+
def get_context_to_cacherule_adapter_factory(rule):
"""Given a cache rule return an adapter factory which expects an object
but only returns the pre-specified cache rule."""
@@ -46,10 +56,13 @@
def __init__(self, registry):
self.registry = registry
-
+
def register(self, obj, rule):
rule = str(rule) # We only want ascii, tyvm
+ if self.explicit and queryUtility(IRulesetType, rule) is None:
+ raise LookupError("Explicit mode set and ruleset %s not found" % rule)
+
factory = get_context_to_cacherule_adapter_factory(rule)
existing = self.directLookup(obj)
if existing is None:
@@ -64,7 +77,6 @@
self.registry.unregisterAdapter(provided=ICacheRule, required=(obj,))
return None
-
def clear(self):
# We force the iterator to be evaluated to start with as the backing
# storage will be changing size
@@ -75,6 +87,16 @@
self.registry.unregisterAdapter(factory=rule.factory,
provided=rule.provided,
required=rule.required)
+
+ for type_ in list(self.registry.registeredUtilities()):
+ if type_.provided != IRulesetType:
+ continue # Not our responsibility
+ else:
+ self.registry.unregisterUtility(component=type_.component,
+ provided=IRulesetType,
+ name=type_.name)
+
+ self.explicit = False
return None
def lookup(self, obj):
@@ -83,13 +105,24 @@
return rule.id
return None
- def enumerate(self):
- seen = set()
- for reg in self.registry.registeredAdapters():
- if reg.provided == ICacheRule and reg.factory.id not in seen:
- yield reg.factory.id
- seen.add(reg.factory.id)
+ __getitem__ = lookup
+ def declareType(self, name, title, description):
+ type_ = RulesetType(name, title, description)
+ self.registry.registerUtility(type_, IRulesetType, name=name)
+
+ def enumerateTypes(self):
+ for name, type_ in getUtilitiesFor(IRulesetType):
+ yield type_
+
+ def _get_explicit(self):
+ return getattr(self.registry, '_z3c_caching_explicit', False)
+ def _set_explicit(self, value):
+ setattr(self.registry, '_z3c_caching_explicit', value)
+ explicit = property(_get_explicit, _set_explicit)
+
+ # Helper methods
+
def directLookup(self, obj):
"""Find a rule _directly_ assigned to `obj`"""
for rule in self.registry.registeredAdapters():
@@ -99,8 +132,6 @@
return rule.factory(None).id
return None
- __getitem__ = lookup
-
def getGlobalRulesetRegistry():
return IRulesetRegistry(getGlobalSiteManager(), None)
@@ -109,13 +140,13 @@
def register(obj, rule):
registry = getGlobalRulesetRegistry()
if registry is None:
- raise LookupError("Global not found initialised")
+ raise LookupError("Global registry initialised")
return registry.register(obj, rule)
def unregister(obj):
registry = getGlobalRulesetRegistry()
if registry is None:
- raise LookupError("Global not found initialised")
+ raise LookupError("Global registry initialised")
return registry.unregister(obj)
def lookup(obj):
@@ -124,4 +155,23 @@
return None
return registry.lookup(obj)
-__all__ = ['getGlobalRulesetRegistry', 'register', 'unregister', 'lookup']
\ No newline at end of file
+def enumerateTypes():
+ registry = getGlobalRulesetRegistry()
+ if registry is None:
+ raise LookupError("Global registry initialised")
+ return registry.enumerateTypes()
+
+def declareType(name, title, description):
+ registry = getGlobalRulesetRegistry()
+ if registry is None:
+ raise LookupError("Global registry initialised")
+ registry.declareType(name, title, description)
+
+def setExplicitMode(mode=True):
+ registry = getGlobalRulesetRegistry()
+ if registry is None:
+ raise LookupError("Global registry initialised")
+ registry.explicit = mode
+
+__all__ = ['getGlobalRulesetRegistry', 'register', 'unregister', 'lookup',
+ 'enumerate', 'declareType', 'setExplicitMode']
Added: z3c.caching/trunk/src/z3c/caching/tests/test3.zcml
===================================================================
--- z3c.caching/trunk/src/z3c/caching/tests/test3.zcml (rev 0)
+++ z3c.caching/trunk/src/z3c/caching/tests/test3.zcml 2010-01-02 09:59:02 UTC (rev 107534)
@@ -0,0 +1,12 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:cache="http://namespaces.zope.org/cache">
+
+ <include package="z3c.caching" file="meta.zcml" />
+
+ <cache:rulesetType
+ name="rule1"
+ title="Rule 1"
+ description="Rule one"
+ />
+
+</configure>
Added: z3c.caching/trunk/src/z3c/caching/tests/test4.zcml
===================================================================
--- z3c.caching/trunk/src/z3c/caching/tests/test4.zcml (rev 0)
+++ z3c.caching/trunk/src/z3c/caching/tests/test4.zcml 2010-01-02 09:59:02 UTC (rev 107534)
@@ -0,0 +1,18 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:cache="http://namespaces.zope.org/cache">
+
+ <include package="z3c.caching" file="meta.zcml" />
+
+ <cache:rulesetType
+ name="rule1"
+ title="Rule 1"
+ description="Rule one"
+ />
+
+ <cache:rulesetType
+ name="rule1"
+ title="Rule 1"
+ description="Rule two"
+ />
+
+</configure>
Added: z3c.caching/trunk/src/z3c/caching/tests/test5.zcml
===================================================================
--- z3c.caching/trunk/src/z3c/caching/tests/test5.zcml (rev 0)
+++ z3c.caching/trunk/src/z3c/caching/tests/test5.zcml 2010-01-02 09:59:02 UTC (rev 107534)
@@ -0,0 +1,17 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:cache="http://namespaces.zope.org/cache">
+
+ <include package="z3c.caching" file="meta.zcml" />
+
+ <cache:ruleset
+ for="z3c.caching.tests.test_registry.ITestView"
+ ruleset="rule1"
+ />
+
+ <cache:rulesetType
+ name="rule1"
+ title="Rule 1"
+ description="Rule one"
+ />
+
+</configure>
Modified: z3c.caching/trunk/src/z3c/caching/tests/test_registry.py
===================================================================
--- z3c.caching/trunk/src/z3c/caching/tests/test_registry.py 2010-01-02 09:37:54 UTC (rev 107533)
+++ z3c.caching/trunk/src/z3c/caching/tests/test_registry.py 2010-01-02 09:59:02 UTC (rev 107534)
@@ -99,18 +99,79 @@
def test_clearing_registry_removes_rulesets(self):
self.registry.register(ITestView, "frop")
+
+ self.registry.clear()
+
i = TestView()
+ self.failUnless(self.registry[i] is None)
+
+ def test_clearing_registry_removes_types(self):
+ self.registry.declareType("rule1", u"Rule 1", u"First rule")
+ self.registry.declareType("rule2", u"Rule 2", u"Second rule")
+
+ self.registry.register(ITestView, "frop")
+
self.registry.clear()
+
+ i = TestView()
self.failUnless(self.registry[i] is None)
+ self.assertEquals(0, len(list(self.registry.enumerateTypes())))
- def test_enumerate(self):
- self.registry.register(ITestView, "rule1")
- self.registry.register(IMoreSpecificTestView, "rule2")
- self.registry.register(OtherTestView, "rule2")
- self.assertEqual(set(['rule1', 'rule2']), set(self.registry.enumerate()))
+ def test_declareType_overrides(self):
+ self.registry.declareType("rule1", u"Rule 1", u"First rule")
+ self.registry.declareType("rule2", u"Rule 2", u"Second rule")
+ self.registry.declareType("rule1", u"Rule One", u"Rule uno")
+
+ rules = list(self.registry.enumerateTypes())
+ rules.sort(lambda x,y: cmp(x.name, y.name))
+
+ self.assertEquals(2, len(rules))
+ self.assertEquals("rule1", rules[0].name)
+ self.assertEquals(u"Rule One", rules[0].title)
+ self.assertEquals(u"Rule uno", rules[0].description)
+ self.assertEquals("rule2", rules[1].name)
+ self.assertEquals(u"Rule 2", rules[1].title)
+ self.assertEquals(u"Second rule", rules[1].description)
+ def test_enumerateTypes(self):
+ self.registry.declareType("rule1", u"Rule 1", u"First rule")
+ self.registry.declareType("rule2", u"Rule 2", u"Second rule")
+
+ rules = list(self.registry.enumerateTypes())
+ rules.sort(lambda x,y: cmp(x.title, y.title))
+
+ self.assertEquals(2, len(rules))
+ self.assertEquals("rule1", rules[0].name)
+ self.assertEquals(u"Rule 1", rules[0].title)
+ self.assertEquals(u"First rule", rules[0].description)
+ self.assertEquals("rule2", rules[1].name)
+ self.assertEquals(u"Rule 2", rules[1].title)
+ self.assertEquals(u"Second rule", rules[1].description)
+
def test_enumerate_empty(self):
- self.assertEqual(set([]), set(self.registry.enumerate()))
+ self.assertEqual(set([]), set(self.registry.enumerateTypes()))
+
+ def test_set_explicit_mode(self):
+ self.registry.explicit = True
+
+ self.assertRaises(LookupError, self.registry.register, TestView, "rule1")
+ self.assertEquals(None, self.registry.lookup(TestView()))
+
+ self.registry.declareType("rule1", u"Rule 1", u"First rule")
+ self.registry.register(TestView, "rule1")
+
+ self.assertEquals("rule1", self.registry.lookup(TestView()))
+
+ def test_disable_explicit_mode(self):
+ self.registry.explicit = True
+
+ self.assertRaises(LookupError, self.registry.register, TestView, "rule1")
+ self.assertEquals(None, self.registry.lookup(TestView()))
+
+ self.registry.explicit = False
+
+ self.registry.register(TestView, "rule1")
+ self.assertEquals("rule1", self.registry.lookup(TestView()))
class TestConvenienceAPI(TestCase):
@@ -156,7 +217,30 @@
self.failUnless(lookup(TestView) is None)
unregister(TestView)
self.failUnless(lookup(TestView) is None)
-
+
+ def test_declareType_enumerateTypes(self):
+ from z3c.caching.registry import declareType, enumerateTypes
+ declareType("rule1", u"Rule 1", u"Rule one")
+
+ rules = list(enumerateTypes())
+ rules.sort(lambda x,y: cmp(x.name, y.name))
+
+ self.assertEquals(1, len(rules))
+ self.assertEquals("rule1", rules[0].name)
+ self.assertEquals(u"Rule 1", rules[0].title)
+ self.assertEquals(u"Rule one", rules[0].description)
+
+ def test_set_explicit_mode(self):
+ from z3c.caching.registry import setExplicitMode
+
+ self.assertEquals(False, self.registry.explicit)
+ setExplicitMode()
+ self.assertEquals(True, self.registry.explicit)
+ setExplicitMode(False)
+ self.assertEquals(False, self.registry.explicit)
+ setExplicitMode(True)
+ self.assertEquals(True, self.registry.explicit)
+
def test_suite():
import unittest
return unittest.defaultTestLoader.loadTestsFromName(__name__)
Modified: z3c.caching/trunk/src/z3c/caching/tests/test_zcml.py
===================================================================
--- z3c.caching/trunk/src/z3c/caching/tests/test_zcml.py 2010-01-02 09:37:54 UTC (rev 107533)
+++ z3c.caching/trunk/src/z3c/caching/tests/test_zcml.py 2010-01-02 09:59:02 UTC (rev 107534)
@@ -34,6 +34,42 @@
def test_conflicting_registrations(self):
zcml = xmlconfig.XMLConfig("test2.zcml", z3c.caching.tests)
self.assertRaises(Exception, zcml) # ZCML conflict error
+
+ def test_declareType(self):
+ zcml = xmlconfig.XMLConfig("test3.zcml", z3c.caching.tests)
+ zcml()
+
+ rules = list(self.registry.enumerateTypes())
+ rules.sort(lambda x,y: cmp(x.name, y.name))
+
+ self.assertEquals(1, len(rules))
+ self.assertEquals("rule1", rules[0].name)
+ self.assertEquals(u"Rule 1", rules[0].title)
+ self.assertEquals(u"Rule one", rules[0].description)
+
+ def test_declareType_multiple(self):
+ zcml = xmlconfig.XMLConfig("test4.zcml", z3c.caching.tests)
+ self.assertRaises(Exception, zcml) # ZCML conflict error
+
+ def test_declareType_explicit_after(self):
+ i = TestView()
+ self.failUnless(self.registry[i] is None)
+
+ self.registry.explicit = True
+
+ zcml = xmlconfig.XMLConfig("test5.zcml", z3c.caching.tests)
+ zcml()
+
+ rules = list(self.registry.enumerateTypes())
+ rules.sort(lambda x,y: cmp(x.name, y.name))
+
+ self.assertEquals(1, len(rules))
+ self.assertEquals("rule1", rules[0].name)
+ self.assertEquals(u"Rule 1", rules[0].title)
+ self.assertEquals(u"Rule one", rules[0].description)
+
+ i = TestView()
+ self.assertEqual(self.registry[i], "rule1")
def test_suite():
import unittest
Modified: z3c.caching/trunk/src/z3c/caching/zcml.py
===================================================================
--- z3c.caching/trunk/src/z3c/caching/zcml.py 2010-01-02 09:37:54 UTC (rev 107533)
+++ z3c.caching/trunk/src/z3c/caching/zcml.py 2010-01-02 09:59:02 UTC (rev 107534)
@@ -16,12 +16,19 @@
default=None,
required=True)
+def rulesetType(_context, name, title, description=u""):
+ declareType = getGlobalRulesetRegistry().declareType
+ _context.action(
+ discriminator=("declareCacheRuleSetType", name),
+ callable=declareType,
+ args=(name, title, description,),
+ order=-10)
+
def ruleset(_context, for_, ruleset):
register = getGlobalRulesetRegistry().register
_context.action(
discriminator=("registerCacheRule", for_),
- callable = register,
- args = (for_, ruleset))
-
-
+ callable=register,
+ args=(for_, ruleset,),
+ order=10)
More information about the checkins
mailing list