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$ """