Permalink
Browse files

intermediate step in combining ContentCategory/ContentCategories; all…

…ow metadata to be specified in the @content constructor
  • Loading branch information...
1 parent fa07154 commit 4db65c43f2af2c13b6a528be8365769e1a867fae @mcdonc mcdonc committed Apr 9, 2012
@@ -19,7 +19,7 @@
logger = logging.getLogger(__name__)
-@content(ICatalog)
+@content(ICatalog, icon='icon-search')
class Catalog(_Catalog):
family = BTrees.family32
def __init__(self, family=None):
@@ -26,27 +26,42 @@ def __init__(self, category_iface):
self.category_iface = category_iface
self.factories = {}
- def add(self, content_iface, factory):
+ def add(self, content_iface, factory, **meta):
self.factories[content_iface] = factory
+ for k, v in meta.items():
+ content_iface.setTaggedValue(k, v)
def create(self, content_iface, *arg, **kw):
return self.factories[content_iface](*arg, **kw)
def provided_by(self, resource):
return self.category_iface.providedBy(resource)
- def all(self, context=_marker):
+ def all(self, context=_marker, **meta):
if context is _marker:
- return self.factories.keys()
- return [i for i in providedBy(context) if i in self.factories]
-
- def first(self, context):
- ifaces = [i for i in providedBy(context) if i in self.factories]
- if not ifaces:
- raise ValueError('%s is not content' % context)
- return ifaces[0]
-
- def get_meta(self, context, name, default=None):
+ candidates = self.factories.keys()
+ else:
+ candidates = [i for i in providedBy(context) if i in self.factories]
+ if not meta:
+ return candidates
+ matches_meta = []
+ for candidate in candidates:
+ ok = True
+ for k, v in meta.items():
+ if not candidate.queryTaggedValue(k) == v:
+ ok = False
+ break
+ if ok:
+ matches_meta.append(candidate)
+ return matches_meta
+
+ def first(self, context, **meta):
+ matching = self.all(context, **meta)
+ if not matching:
+ raise ValueError('No match!')
+ return matching[0]
+
+ def metadata(self, context, name, default=None):
content_ifaces = self.all(context)
for iface in content_ifaces:
maybe = iface.queryTaggedValue(name, default)
@@ -58,14 +73,15 @@ class ContentCategories(object):
def __init__(self):
self.categories = {}
- def add(self, content_iface, factory, category_iface=None):
+ def add(self, content_iface, factory, **meta):
+ category_iface = meta.get('category')
if category_iface is None:
category_iface = IContent
if category_iface is not None:
addbase(content_iface, category_iface)
category = self.categories.setdefault(category_iface,
ContentCategory(category_iface))
- category.add(content_iface, factory)
+ category.add(content_iface, factory, **meta)
def __getitem__(self, category_iface):
return self.categories[category_iface]
@@ -76,44 +92,45 @@ def create(self, content_iface, *arg, **kw):
def provided_by(self, resource):
return self.categories[IContent].provided_by(resource)
- def all(self, context=_marker):
- return self.categories[IContent].all(context)
+ def all(self, context=_marker, **meta):
+ return self.categories[IContent].all(context, **meta)
- def first(self, context):
- return self.categories[IContent].first(context)
+ def first(self, context, **meta):
+ return self.categories[IContent].first(context, **meta)
- def get_meta(self, context, name, default=None):
- return self.categories[IContent].get_meta(context, name, default)
+ def metadata(self, context, name, default=None):
+ return self.categories[IContent].metadata(context, name, default)
# venusian decorator that marks a class as a content class
class content(object):
venusian = venusian
- def __init__(self, content_iface, category_iface=None):
+ def __init__(self, content_iface, **meta):
self.content_iface = content_iface
- self.category_iface = category_iface
+ self.meta = meta
def __call__(self, wrapped):
implementer(self.content_iface)(wrapped)
- settings = dict(category_iface=self.category_iface)
def callback(context, name, ob):
config = context.config.with_package(info.module)
- config.add_content_type(self.content_iface, wrapped, **settings)
+ config.add_content_type(self.content_iface, wrapped, **self.meta)
info = self.venusian.attach(wrapped, callback, category='substanced')
- settings['_info'] = info.codeinfo # fbo "action_method"
+ self.meta['_info'] = info.codeinfo # fbo "action_method"
return wrapped
-def add_content_type(config, content_iface, factory, category_iface=None):
+def add_content_type(config, content_iface, factory, **meta):
if not IInterface.providedBy(content_iface):
raise ConfigurationError(
'The provided "content_iface" argument (%r) is not an '
'interface object (it does not inherit from '
'zope.interface.Interface' % type)
+ category_iface = meta.get('category')
+
if category_iface is not None:
if not IInterface.providedBy(category_iface):
raise ConfigurationError(
- 'The provided "category_iface" argument (%r) is not an '
+ 'The provided "category" argument (%r) is not an '
'interface object (it does not inherit from '
'zope.interface.Interface' % type)
@@ -122,7 +139,7 @@ def add_content_type(config, content_iface, factory, category_iface=None):
implementer(content_iface)(factory)
def register_factory():
- config.registry.content.add(content_iface, factory, category_iface)
+ config.registry.content.add(content_iface, factory, **meta)
discrim = ('content-type', content_iface, category_iface)
config.action(discrim, callable=register_factory)
@@ -45,6 +45,14 @@ def test_add(self):
inst.add(IDummy, True)
self.assertEqual(inst.factories[IDummy], True)
+ def test_add_with_meta(self):
+ inst = self._makeOne(ICategory)
+ class IFoo(Interface):
+ pass
+ inst.add(IFoo, True, icon='fred')
+ self.assertEqual(inst.factories[IFoo], True)
+ self.assertEqual(IFoo.getTaggedValue('icon'), 'fred')
+
def test_create(self):
inst = self._makeOne(ICategory)
inst.factories[IDummy] = lambda a: a
@@ -96,23 +104,23 @@ def test_first_noprovides(self):
dummy = Dummy()
self.assertRaises(ValueError, inst.first, dummy)
- def test_get_meta(self):
+ def test_metadata(self):
inst = self._makeOne(ICategory)
inst.factories[IDummy] = True
inst.factories[ICategory] = True
dummy = Dummy()
alsoProvides(dummy, IDummy)
alsoProvides(dummy, ICategory)
- self.assertEqual(inst.get_meta(dummy, 'icon'), 'icon-name')
+ self.assertEqual(inst.metadata(dummy, 'icon'), 'icon-name')
- def test_get_meta_notfound(self):
+ def test_metadata_notfound(self):
inst = self._makeOne(ICategory)
inst.factories[IDummy] = True
inst.factories[ICategory] = True
dummy = Dummy()
alsoProvides(dummy, IDummy)
alsoProvides(dummy, ICategory)
- self.assertEqual(inst.get_meta(dummy, 'doesntexist'), None)
+ self.assertEqual(inst.metadata(dummy, 'doesntexist'), None)
class TestContentCategories(unittest.TestCase):
def _makeOne(self):
@@ -127,7 +135,6 @@ class Factory(object):
pass
inst.add(IFoo, Factory)
self.assertEqual(inst.categories[IContent].factories[IFoo], Factory)
- ## self.assertTrue(IFoo.providedBy(Factory()))
self.assertTrue(IContent in IFoo.__iro__)
self.assertTrue(IContent in IFoo.__bases__)
@@ -137,9 +144,8 @@ class IFoo(Interface):
pass
class Factory(object):
pass
- inst.add(IFoo, Factory, ICategory)
+ inst.add(IFoo, Factory, category=ICategory)
self.assertEqual(inst.categories[ICategory].factories[IFoo], Factory)
- ## self.assertTrue(IFoo.providedBy(Factory()))
self.assertTrue(ICategory in IFoo.__iro__)
self.assertTrue(ICategory in IFoo.__bases__)
@@ -179,11 +185,11 @@ def test_all_no_context(self):
inst.categories[IContent] = DummyCategory(None)
self.assertEqual(inst.all(), [])
- def test_get_meta(self):
+ def test_metadata(self):
inst = self._makeOne()
inst.categories[IContent] = DummyCategory(None)
dummy = Dummy()
- self.assertEqual(inst.get_meta(dummy, 'abc'), 'abc')
+ self.assertEqual(inst.metadata(dummy, 'abc'), 'abc')
class Test_content(unittest.TestCase):
def _makeOne(self, iface):
@@ -222,20 +228,24 @@ def _callFUT(self, *arg, **kw):
def test_content_iface_not_IInterface(self):
from pyramid.exceptions import ConfigurationError
self.assertRaises(
- ConfigurationError, self._callFUT, None, object(), None, IDummy)
+ ConfigurationError,
+ self._callFUT,
+ None, object(), None, category=IDummy)
def test_category_iface_not_IInterface(self):
from pyramid.exceptions import ConfigurationError
self.assertRaises(
- ConfigurationError, self._callFUT, None, IDummy, None, object())
+ ConfigurationError,
+ self._callFUT,
+ None, IDummy, None, category=object())
def test_success(self):
def factory(): pass
config = DummyConfig()
config.registry.content = DummyContentCategories()
class IFoo(Interface):
pass
- self._callFUT(config, IFoo, factory, ICategory)
+ self._callFUT(config, IFoo, factory, category=ICategory)
self.assertEqual(len(config.actions), 1)
self.assertEqual(
config.actions[0][0],
@@ -244,14 +254,14 @@ class IFoo(Interface):
config.actions[0][1]['callable']()
self.assertEqual(
config.registry.content.added,
- [(IFoo, factory, ICategory)])
+ [((IFoo, factory), {'category':ICategory})])
class DummyContentCategories(object):
def __init__(self):
self.added = []
- def add(self, *arg):
- self.added.append(arg)
+ def add(self, *arg, **meta):
+ self.added.append((arg, meta))
class DummyCategory(object):
def __init__(self, result):
@@ -272,7 +282,7 @@ def all(self, context=None):
def first(self, context):
return None
- def get_meta(self, context, name, default=None):
+ def metadata(self, context, name, default=None):
return name
class ICategory(Interface):
@@ -4,8 +4,6 @@
from BTrees.OOBTree import OOBTree
from BTrees.Length import Length
-from zope.interface import implementer
-
from ..interfaces import (
IFolder,
marker,
@@ -23,8 +21,7 @@
from ..service import find_service
-@implementer(IFolder)
-@content(IFolder)
+@content(IFolder, icon='icon-folder-close')
class Folder(Persistent):
""" A folder implementation which acts much like a Python dictionary.
View
@@ -3,7 +3,6 @@
from zope.interface import (
Interface,
Attribute,
- taggedValue,
)
class IContent(Interface):
@@ -33,7 +32,6 @@ def set_properties(struct):
class IObjectMap(Interface):
""" A map of objects to paths """
- taggedValue('icon', 'icon-asterisk')
def objectid_for(obj_or_path_tuple):
""" Return the object id for obj_or_path_tuple """
def path_for(objectid):
@@ -61,11 +59,8 @@ class ICatalog(Interface):
objectids = Attribute(
'a sequence of objectids that are cataloged in this catalog')
- taggedValue('icon', 'icon-search')
-
class ISite(IPropertied):
""" Marker interface for something that is the root of a site """
- taggedValue('icon', 'icon-home')
class ISearch(Interface):
""" Adapter for searching the catalog """
@@ -106,9 +101,6 @@ class IFolder(Interface):
name to either be Unicode or a byte string decodable using the
default system encoding or the UTF-8 encoding."""
- taggedValue('icon', 'icon-folder-close')
-
-
order = Attribute("""Order of items within the folder
(Optional) If not set on the instance, objects are iterated in an
arbitrary order based on the underlying data store.""")
@@ -237,23 +229,18 @@ def remove(name, send_events=True):
class IUser(IPropertied):
""" Marker interface representing a user """
- taggedValue('icon', 'icon-user')
class IGroup(IPropertied):
""" Marker interface representing a group """
- taggedValue('icon', 'icon-th-list')
class IUsers(Interface):
""" Marker interface representing a collection of users """
- taggedValue('icon', 'icon-list-alt')
class IGroups(Interface):
""" Marker interface representing a collection of groups """
- taggedValue('icon', 'icon-list-alt')
class IPrincipals(Interface):
""" Marker interface representing a container of users and groups """
- taggedValue('icon', 'icon-lock')
marker = object()
@@ -83,7 +83,7 @@
_marker = object()
-@content(IObjectMap)
+@content(IObjectMap, icon='icon-asterisk')
class ObjectMap(Persistent):
_v_nextid = None
Oops, something went wrong.

0 comments on commit 4db65c4

Please sign in to comment.