diff --git a/__init__.py b/__init__.py
index 45303e9..fec71a7 100644
--- a/__init__.py
+++ b/__init__.py
@@ -23,38 +23,4 @@
$Id$
"""
-from zope.app import zapi
-from zope.app.folder.folder import Folder
-from zope.interface import implements
-from interfaces import ILoops
-
-loopsPrefix = '.loops'
-
-class Loops(Folder):
-
- implements(ILoops)
-
- _skinName = ''
- def getSkinName(self): return self._skinName
- def setSkinName(self, skinName): self._skinName = skinName
- skinName = property(getSkinName, setSkinName)
-
- def getLoopsRoot(self):
- return self
-
- def getConceptManager(self):
- return self['concepts']
-
- def getResourceManager(self):
- return self['resources']
-
- def getViewManager(self):
- return self['views']
-
- def getLoopsUri(self, obj):
- return str(loopsPrefix + zapi.getPath(obj)[len(zapi.getPath(self)):])
-
- def loopsTraverse(self, uri):
- prefix = loopsPrefix + '/'
- if uri.startswith(prefix):
- return zapi.traverse(self, uri[len(prefix):])
+from loops.base import Loops
diff --git a/base.py b/base.py
new file mode 100644
index 0000000..babc7fc
--- /dev/null
+++ b/base.py
@@ -0,0 +1,63 @@
+# -*- coding: UTF-8 -*-
+# -*- Mode: Python; py-indent-offset: 4 -*-
+#
+# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+
+$Id$
+"""
+
+from zope.app.folder.folder import Folder
+from zope.app.traversing.api import getPath, traverse
+from zope.interface import implements
+
+from loops.interfaces import ILoops
+
+loopsPrefix = '.loops'
+
+
+class Loops(Folder):
+
+ implements(ILoops)
+
+ _skinName = ''
+ def getSkinName(self): return self._skinName
+ def setSkinName(self, skinName): self._skinName = skinName
+ skinName = property(getSkinName, setSkinName)
+
+ def getLoopsRoot(self):
+ return self
+
+ def getConceptManager(self):
+ return self['concepts']
+
+ def getResourceManager(self):
+ return self['resources']
+
+ def getViewManager(self):
+ return self['views']
+
+ def getLoopsUri(self, obj):
+ return str(loopsPrefix + getPath(obj)[len(getPath(self)):])
+
+ def loopsTraverse(self, uri):
+ prefix = loopsPrefix + '/'
+ if uri.startswith(prefix):
+ return traverse(self, uri[len(prefix):])
+
diff --git a/configure.zcml b/configure.zcml
index 408316a..b8d4c4b 100644
--- a/configure.zcml
+++ b/configure.zcml
@@ -388,5 +388,6 @@
+
diff --git a/rest/README.txt b/rest/README.txt
new file mode 100755
index 0000000..d531460
--- /dev/null
+++ b/rest/README.txt
@@ -0,0 +1,77 @@
+===============================================================
+loops.xmlrpc
+===============================================================
+
+ ($Id$)
+
+Let's do some basic set up
+
+ >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
+ >>> site = placefulSetUp(True)
+
+ >>> from zope import component, interface
+ >>> from zope.publisher.browser import TestRequest
+
+and setup a simple loops site with a concept manager and some concepts
+(with all the type machinery, what in real life is done via standard
+ZCML setup):
+
+ >>> from cybertools.relation.registry import DummyRelationRegistry
+ >>> component.provideUtility(DummyRelationRegistry())
+ >>> from cybertools.relation.tests import IntIdsStub
+ >>> component.provideUtility(IntIdsStub())
+
+ >>> from loops.type import ConceptType, TypeConcept
+ >>> component.provideAdapter(ConceptType)
+ >>> component.provideAdapter(TypeConcept)
+
+ >>> from loops import Loops
+ >>> loopsRoot = site['loops'] = Loops()
+
+ >>> from loops.setup import SetupManager
+ >>> setup = SetupManager(loopsRoot)
+ >>> concepts, resources, views = setup.setup()
+
+Let's look what setup has provided us with:
+
+ >>> list(concepts)
+ [u'file', u'hasType', u'image', u'predicate', u'standard', u'textdocument', u'type']
+
+
+loops Traversal
+===============
+
+The loops root object provides a traversal mechanism that goes beyond the
+standard container traversal. One can directly access objects by their
+unique id or via a symbolic name (like `startObject`); usually the traverser
+returns a REST view of the object.
+
+ >>> from loops.rest.traversal import LoopsTraverser
+ >>> from zope.publisher.interfaces.browser import IBrowserPublisher
+ >>> component.provideAdapter(LoopsTraverser, provides=IBrowserPublisher)
+
+ >>> from loops.rest.common import ConceptView
+ >>> component.provideAdapter(ConceptView)
+
+Navigation typically starts at a start object, which by default ist the
+top-level type concept:
+
+ >>> request = TestRequest()
+ >>> obj = LoopsTraverser(loopsRoot, request).publishTraverse(request, 'startObject')
+ >>> obj
+
+ >>> obj.context.title
+ u'Type'
+
+The traversal adapter returns a view that when called renders the
+representation of its context object:
+
+ >>> obj()
+ 'Hello REST'
+
+
+Fin de partie
+=============
+
+ >>> placefulTearDown()
+
diff --git a/rest/__init__.py b/rest/__init__.py
new file mode 100644
index 0000000..4bc90fb
--- /dev/null
+++ b/rest/__init__.py
@@ -0,0 +1,4 @@
+"""
+$Id$
+"""
+
diff --git a/rest/common.py b/rest/common.py
new file mode 100644
index 0000000..b02f51d
--- /dev/null
+++ b/rest/common.py
@@ -0,0 +1,59 @@
+#
+# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+Common stuff for REST (REpresentational State Transfer) views.
+
+$Id$
+"""
+
+from zope.interface import implements
+from zope.component import adapts
+from zope.publisher.interfaces.browser import IBrowserRequest, IBrowserPublisher
+
+from loops.interfaces import IConcept
+
+# interfaces
+
+class IRESTView(IBrowserPublisher):
+ """ The basic interface for all REST views.
+ """
+
+ def __call__():
+ """ Render the representation.
+ """
+
+
+class ConceptView(object):
+ """ A REST view for a concept.
+ """
+
+ implements(IRESTView)
+ adapts(IConcept, IBrowserRequest)
+
+ def __init__(self, context, request):
+ self.context = self.__parent__ = context
+ self.request = request
+
+ def __call__(self):
+ return 'Hello REST'
+
+ def browserDefault(self, request):
+ """ Make the publisher happy.
+ """
+ return self, ()
diff --git a/rest/configure.zcml b/rest/configure.zcml
new file mode 100644
index 0000000..8a3e2b3
--- /dev/null
+++ b/rest/configure.zcml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rest/tests.py b/rest/tests.py
new file mode 100755
index 0000000..70be7b1
--- /dev/null
+++ b/rest/tests.py
@@ -0,0 +1,22 @@
+# $Id$
+
+import unittest, doctest
+from zope.testing.doctestunit import DocFileSuite
+
+
+class Test(unittest.TestCase):
+ "Basic tests for the loops.rest package."
+
+ def testBasics(self):
+ pass
+
+
+def test_suite():
+ flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
+ return unittest.TestSuite((
+ unittest.makeSuite(Test),
+ DocFileSuite('README.txt', optionflags=flags),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/rest/traversal.py b/rest/traversal.py
new file mode 100644
index 0000000..915ae55
--- /dev/null
+++ b/rest/traversal.py
@@ -0,0 +1,54 @@
+# -*- coding: UTF-8 -*-
+# -*- Mode: Python; py-indent-offset: 4 -*-
+#
+# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+
+$Id$
+"""
+
+from zope import component
+from zope.component import adapts
+from zope.app.container.traversal import ItemTraverser
+from zope.publisher.interfaces.browser import IBrowserRequest
+
+from loops.interfaces import ILoops
+from loops import util
+from loops.rest.common import IRESTView
+
+
+class LoopsTraverser(ItemTraverser):
+
+ adapts(ILoops, IBrowserRequest)
+
+ restProperties = ('startObject', 'typePredicate',)
+
+ def publishTraverse(self, request, name):
+ if name in self.restProperties:
+ obj = getattr(self, name)
+ return component.getMultiAdapter((obj, request), IRESTView)
+ if name.isdigit():
+ obj = util.getObjectForUid(int(name))
+ return component.getMultiAdapter((obj, request), IRESTView)
+ return super(LoopsTraverser, self).publishTraverse(request, name)
+
+ @property
+ def startObject(self):
+ cm = self.context.getConceptManager()
+ return cm.get('domain', cm.getTypeConcept())
diff --git a/xmlrpc/common.py b/xmlrpc/common.py
index 29ba3e6..c94ce36 100644
--- a/xmlrpc/common.py
+++ b/xmlrpc/common.py
@@ -17,7 +17,7 @@
#
"""
-Tournament and Assessment views.
+XML-RPC views.
$Id$
"""