[Checkins] SVN: grok/trunk/ Added an ordered container component.
Jan-Wijbrand Kolman
janwijbrand at gmail.com
Thu May 1 10:03:06 EDT 2008
Log message for revision 86001:
Added an ordered container component.
Changed:
U grok/trunk/CHANGES.txt
U grok/trunk/src/grok/__init__.py
U grok/trunk/src/grok/components.py
U grok/trunk/src/grok/interfaces.py
A grok/trunk/src/grok/tests/container/orderedcontainer.py
A grok/trunk/src/grok/tests/container/orderedcontainerfiresevent.py
-=-
Modified: grok/trunk/CHANGES.txt
===================================================================
--- grok/trunk/CHANGES.txt 2008-05-01 13:45:49 UTC (rev 86000)
+++ grok/trunk/CHANGES.txt 2008-05-01 14:03:05 UTC (rev 86001)
@@ -7,10 +7,12 @@
Feature changes
---------------
+* Added an OrderedContainer component.
+
* Merged the versions from the 3.4 KGS:
http://download.zope.org/zope3.4/versions-3.4.0c1.cfg
- We are now using the latest Zope 3 releases for all the Zope
+ We are now using the latest Zope 3 releases for all the Zope
For upgrade notes, see doc/upgrade.txt for more information.
* Added support for easier testsetup based on z3c.testsetup. This is a
Modified: grok/trunk/src/grok/__init__.py
===================================================================
--- grok/trunk/src/grok/__init__.py 2008-05-01 13:45:49 UTC (rev 86000)
+++ grok/trunk/src/grok/__init__.py 2008-05-01 14:03:05 UTC (rev 86001)
@@ -33,7 +33,8 @@
from grokcore.component import Adapter, MultiAdapter, GlobalUtility
from grok.components import Model, View
from grok.components import XMLRPC, REST, JSON
-from grok.components import PageTemplate, PageTemplateFile, Container, Traverser
+from grok.components import PageTemplate, PageTemplateFile, Traverser
+from grok.components import Container, OrderedContainer
from grok.components import Site, LocalUtility, Annotation
from grok.components import Application, Form, AddForm, EditForm, DisplayForm
from grok.components import Indexes
Modified: grok/trunk/src/grok/components.py
===================================================================
--- grok/trunk/src/grok/components.py 2008-05-01 13:45:49 UTC (rev 86000)
+++ grok/trunk/src/grok/components.py 2008-05-01 14:03:05 UTC (rev 86001)
@@ -43,6 +43,9 @@
from zope.app.container.btree import BTreeContainer
from zope.app.container.contained import Contained
from zope.app.container.interfaces import IReadContainer, IObjectAddedEvent
+from zope.app.container.interfaces import IOrderedContainer
+from zope.app.container.contained import notifyContainerModified
+from persistent.list import PersistentList
from zope.app.component.site import SiteManagerContainer
from zope.app.component.site import LocalSiteManager
@@ -67,6 +70,47 @@
interface.implements(IAttributeAnnotatable)
+class OrderedContainer(Container):
+ interface.implements(IOrderedContainer)
+
+ def __init__(self):
+ super(OrderedContainer, self).__init__()
+ self._order = PersistentList()
+
+ def keys(self):
+ # Return a copy of the list to prevent accidental modifications.
+ return self._order[:]
+
+ def __iter__(self):
+ return iter(self.keys())
+
+ def values(self):
+ return (self[key] for key in self._order)
+
+ def items(self):
+ return ((key, self[key]) for key in self._order)
+
+ def __setitem__(self, key, object):
+ foo = self.has_key(key)
+ # Then do whatever containers normally do.
+ super(OrderedContainer, self).__setitem__(key, object)
+ if not foo:
+ self._order.append(key)
+
+ def __delitem__(self, key):
+ # First do whatever containers normally do.
+ super(OrderedContainer, self).__delitem__(key)
+ self._order.remove(key)
+
+ def updateOrder(self, order):
+ if set(order) != set(self._order):
+ raise ValueError("Incompatible key set.")
+
+ self._order = PersistentList()
+ self._order.extend(order)
+ notifyContainerModified(self)
+
+
class Site(SiteManagerContainer):
pass
@@ -616,7 +660,7 @@
"""See zope.contentprovider.interfaces.IContentProvider"""
# Now render the view
if self.template:
- return self.template.render(self)
+ return self.template.render(self)
else:
viewlets = grokcore.component.util.sort_components(self.viewlets)
return u'\n'.join([viewlet.render() for viewlet in viewlets])
Modified: grok/trunk/src/grok/interfaces.py
===================================================================
--- grok/trunk/src/grok/interfaces.py 2008-05-01 13:45:49 UTC (rev 86000)
+++ grok/trunk/src/grok/interfaces.py 2008-05-01 14:03:05 UTC (rev 86001)
@@ -28,6 +28,7 @@
Model = interface.Attribute("Base class for persistent content objects "
"(models).")
Container = interface.Attribute("Base class for containers.")
+ OrderedContainer = interface.Attribute("Base class for ordered containers.")
Site = interface.Attribute("Mixin class for sites.")
Application = interface.Attribute("Base class for applications.")
Adapter = interface.Attribute("Base class for adapters.")
@@ -162,7 +163,7 @@
def order(value=None):
"""Control the ordering of components.
- If the value is specified, the order will be determined by sorting on
+ If the value is specified, the order will be determined by sorting on
it.
If no value is specified, the order will be determined by definition
order within the module.
@@ -173,7 +174,7 @@
Inter-module order is by dotted name of the module the
components are in; unless an explicit argument is specified to
``grok.order()``, components are grouped by module.
-
+
The function grok.util.sort_components can be used to sort
components according to these rules.
"""
@@ -441,7 +442,7 @@
request = interface.Attribute("Request that REST handler was looked"
"up with.")
-
+
body = interface.Attribute(
"""The text of the request body.""")
@@ -468,22 +469,22 @@
"""
class ITemplateFileFactory(interface.Interface):
- """Utility that generates templates from files in template directories.
+ """Utility that generates templates from files in template directories.
"""
-
+
def __call__(filename, _prefix=None):
"""Creates an ITemplate from a file
-
+
_prefix is the directory the file is located in
"""
class ITemplate(interface.Interface):
"""Template objects
"""
-
+
def _initFactory(factory):
"""Template language specific initializations on the view factory."""
-
+
def render(view):
"""Renders the template"""
Added: grok/trunk/src/grok/tests/container/orderedcontainer.py
===================================================================
--- grok/trunk/src/grok/tests/container/orderedcontainer.py (rev 0)
+++ grok/trunk/src/grok/tests/container/orderedcontainer.py 2008-05-01 14:03:05 UTC (rev 86001)
@@ -0,0 +1,78 @@
+"""
+
+The grok.OrderedContainer is a a model that is also a container. Unlike plain
+grok.Containers, OrderedContainers keep the mapping keys in the order items
+were added. It has a dictionary API. It in fact stores its information in a
+BTree so you can store a lot of items in a scalable way.
+
+ >>> grok.testing.grok(__name__)
+
+ >>> from zope.app.container.interfaces import IContainer
+ >>> bones = OrderedBones()
+ >>> IContainer.providedBy(bones)
+ True
+ >>> from zope.app.container.interfaces import IOrderedContainer
+ >>> IOrderedContainer.providedBy(bones)
+ True
+ >>> from zope.app.container.btree import BTreeContainer
+ >>> isinstance(bones, BTreeContainer)
+ True
+
+Order is initially determined by the sequence in which items were added (mind
+that there is a subscriber to the containermodified event in order to
+illustrate an ordered container fires events just like normal containers)::
+
+ >>> bones['thigh'] = Bone('Thigh Bone')
+ >>> bones['knee'] = Bone('Knee Cap')
+ >>> bones['shin'] = Bone('Shin Bone')
+ >>> bones['ankle'] = Bone('Ankle Joint')
+ >>> bones.keys()
+ ['thigh', 'knee', 'shin', 'ankle']
+
+Now change the order::
+
+ >>> bones.updateOrder(order=['ankle', 'shin', 'knee', 'thigh'])
+ >>> bones.keys()
+ ['ankle', 'shin', 'knee', 'thigh']
+
+ >>> list(bones.items())
+ [('ankle', <grok.tests.container.orderedcontainer.Bone object at ...>),
+ ('shin', <grok.tests.container.orderedcontainer.Bone object at ...>),
+ ('knee', <grok.tests.container.orderedcontainer.Bone object at ...>),
+ ('thigh', <grok.tests.container.orderedcontainer.Bone object at ...>)]
+
+ >>> [bone.name for bone in bones.values()]
+ ['Ankle Joint', 'Shin Bone', 'Knee Cap', 'Thigh Bone']
+
+ >>> del bones['knee']
+ >>> bones.keys()
+ ['ankle', 'shin', 'thigh']
+
+ >>> bones['toe'] = Bone('Toe')
+ >>> bones.keys()
+ ['ankle', 'shin', 'thigh', 'toe']
+
+Adding a new object under an existing key, raises a DuplicationError::
+
+ >>> bones['shin'] = Bone('Another Shin Bone')
+ Traceback (most recent call last):
+ ...
+ DuplicationError: shin
+
+Reordering with a wrong set of keys should fail::
+
+ >>> bones.updateOrder(order=['ankle', 'shin', 'knee', 'thigh'])
+ Traceback (most recent call last):
+ ...
+ ValueError: Incompatible key set.
+
+"""
+
+import grok
+
+class OrderedBones(grok.OrderedContainer):
+ pass
+
+class Bone(grok.Model):
+ def __init__(self, name):
+ self.name = name
Added: grok/trunk/src/grok/tests/container/orderedcontainerfiresevent.py
===================================================================
--- grok/trunk/src/grok/tests/container/orderedcontainerfiresevent.py (rev 0)
+++ grok/trunk/src/grok/tests/container/orderedcontainerfiresevent.py 2008-05-01 14:03:05 UTC (rev 86001)
@@ -0,0 +1,39 @@
+"""
+Mind that there is a subscriber to the containermodified event in order to
+illustrate an ordered container fires events just like normal containers::
+
+ >>> grok.testing.grok(__name__)
+ >>> bones = OrderedBones()
+
+Add an item::
+
+ >>> bones['thigh'] = Bone('Thigh Bone')
+ Container has changed!
+
+Now change the order::
+
+ >>> bones.updateOrder(order=['thigh'])
+ Container has changed!
+
+Delete an item::
+
+ >>> del bones['thigh']
+ Container has changed!
+ >>> bones.keys()
+ []
+
+"""
+
+import grok
+
+class OrderedBones(grok.OrderedContainer):
+ pass
+
+class Bone(grok.Model):
+ def __init__(self, name):
+ self.name = name
+
+from zope.app.container.interfaces import IContainerModifiedEvent
+ at grok.subscribe(OrderedBones, IContainerModifiedEvent)
+def container_changed(object, event):
+ print 'Container has changed!'
More information about the Checkins
mailing list