[Zope3-checkins] CVS: Zope3/src/zope/app/index - __init__.py:1.1.2.1 configure.zcml:1.1.2.1 processors.py:1.1.2.1 queries.py:1.1.2.1 subscribers.py:1.1.2.1 subscription_control.pt:1.1.2.1

Jim Fulton jim@zope.com
Mon, 23 Dec 2002 14:31:40 -0500


Update of /cvs-repository/Zope3/src/zope/app/index
In directory cvs.zope.org:/tmp/cvs-serv19908/zope/app/index

Added Files:
      Tag: NameGeddon-branch
	__init__.py configure.zcml processors.py queries.py 
	subscribers.py subscription_control.pt 
Log Message:
Initial renaming before debugging

=== Added File Zope3/src/zope/app/index/__init__.py ===
#
# This file is necessary to make this directory a package.


=== Added File Zope3/src/zope/app/index/configure.zcml ===
<zopeConfigure
   xmlns="http://namespaces.zope.org/zope"
   xmlns:browser='http://namespaces.zope.org/browser'
   >

  <include package=".text" />


  <content class="zope.app.index.subscribers.Registration">
    <require
      permission="Zope.ManageServices"
      interface="zope.app.indexzope.app.indexzope.app.index.subscribers.ISubscriptionControl"
      />
    <factory
      id="Zope.App.indexzope.app.index.subscribers.Registration"
      permission="Zope.ManageServices"
      />
  </content>

  <browser:menuItem
    menu="add_component"
    for="zope.app.interfaces.container.IAdding"
    action="Zope.App.indexzope.app.index.subscribers.Registration"
    title="Registration subscriber"
    description="An event subscriber that registers content with the objecthub"
    />

  <browser:defaultView
    for="zope.app.indexzope.app.indexzope.app.index.subscribers.ISubscriptionControl"
    name="control.html" />

  <browser:view
    for="zope.app.indexzope.app.indexzope.app.index.subscribers.ISubscriptionControl"
    permission="Zope.ManageServices"
    name="control.html"
    template="subscription_control.pt" />

</zopeConfigure>


=== Added File Zope3/src/zope/app/index/processors.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Generic query processors for use with multiple indexes..

$Id: processors.py,v 1.1.2.1 2002/12/23 19:31:37 jim Exp $
"""

from __future__ import generators

from zope.app.interfaces.index.interfaces import IRankedObjectIterator, IRankedObjectRecord, \
    IRankedHubIdList, IBatchedResult
from zope.app.interfaces.services.query import IQueryProcessor

from zope.component import getAdapter, getService
from zope.proxy.context import ContextMethod



class ObjectRetrievingProcessor:
    """Converts a RankedHubIdList into an iteratable
       list of ranked objects by retrieving the objects
       from the ObjectHub.
    """

    __implements__ = IQueryProcessor
    
    input_interface = IRankedHubIdList, IBatchedResult
    output_interface = IRankedObjectIterator

    def __call__(wrapped_self, query):
        list = getAdapter(query, IRankedHubIdList)
        batch = getAdapter(query, IBatchedResult)

        objectHub = getService(wrapped_self, "ObjectHub")
        
        # XXX do we need wrapping for the objects returned by the hub?
        iterator = RankedObjectIterator(list, objectHub.getObject, batch.startPosition, 
                                        batch.batchSize, batch.totalSize)

        return iterator
    __call__ = ContextMethod(__call__)
            
class RankedObjectIterator:
    """Iterates over a given list of IRankedObjectRecord."""

    __implements__ = IRankedObjectIterator, IBatchedResult

    def __init__(self, recordlist, objectfetcher, startposition, batchsize, totalsize):
        self._records = recordlist
        self.startPosition = startposition
        self.batchSize = batchsize
        self.totalSize = totalsize
        self.__objectfetcher = objectfetcher

    def __iter__(self):
        objectfetcher = self.__objectfetcher
        
        for hubid, rank in self._records:
            # XXX maybe we should catch some exceptions like security related
            # ones or NotFoundError, to avoid breaking the iteration. Think
            # about yielding an NotFound-Indicator in such a case.
            yield RankedObjectRecord(objectfetcher(hubid), rank)
        raise StopIteration

class RankedObjectRecord:
    """Contains a reference to a ranked object."""

    __slots__ = ["rank", "object"]

    __implements__ = IRankedObjectRecord

    def __init__(self, object, rank):
        self.rank = rank
        self.object = object



=== Added File Zope3/src/zope/app/index/queries.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Generic queries for indexes.

$Id: queries.py,v 1.1.2.1 2002/12/23 19:31:37 jim Exp $
"""

from zope.component import getAdapter
from zope.app.interfaces.index.interfaces import IBatchedResult, IRankedHubIdList

class BatchedRankedResult:

    __implements__ = IBatchedResult, IRankedHubIdList

    def __init__(self, hubidlist, startposition, batchsize, totalsize):
        self.__hubidlist = hubidlist
        self.startPosition = startposition
        self.batchSize = batchsize
        self.totalSize = totalsize

    def __getitem__(self, index):
        return self.__hubidlist[index]


=== Added File Zope3/src/zope/app/index/subscribers.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""A stupid registration thingie.

In order to do indexing, objects need to be registered with the object
hub.  A real site should define a policy for when objects are to be
registered.  This particular implementation subscribes to
IObjectAddedEvent events from the event service, and registers
absolutely everything.   It also has a way of registering all
pre-existing objects.

XXX This is really just an example!  There are no unit tests, and it
hardcodes all the policy decisions.  Also, it has some "viewish"
properties.  The traversal code in registerExisting could be useful
for creating a general "Find" facility like the Zope2 Find tab.

$Id: subscribers.py,v 1.1.2.1 2002/12/23 19:31:37 jim Exp $
"""
__metaclass__ = type

from zope.interface import Interface
from persistence import Persistent

from zope.interfaces.event import ISubscriber
from zope.app.interfaces.event import IObjectAddedEvent
from zope.app.content.folder import IFolder
from zope.proxy.context import ContextMethod
from zope.component import getService, queryAdapter

from Zope.App.Traversing import traverse, traverseName, \
     getPhysicalPath, getPhysicalRoot
from zope.app.interfaces.services.hub import ObjectHubError

class ISubscriptionControl(Interface):
    def subscribe():
        """Subscribe to the prevailing object hub service."""

    def unsubscribe():
        """Unsubscribe from the object hub service."""

    def isSubscribed():
        """Return whether we are currently subscribed."""

    def registerExisting():
        """Register all existing objects (for some definition of all)."""

class Registration(Persistent):

    __implements__ = ISubscriptionControl, ISubscriber

    def notify(wrapped_self, event):
        """An event occured. Perhaps register this object with the hub."""
        hub = getService(wrapped_self, "ObjectHub")
        wrapped_self._registerObject(event.object, hub)
    notify = ContextMethod(notify)

    currentlySubscribed = False # Default subscription state

    def subscribe(wrapped_self):
        if wrapped_self.currentlySubscribed:
            raise RuntimeError, "already subscribed; please unsubscribe first"
        channel = wrapped_self._getChannel(None)
        channel.subscribe(wrapped_self, IObjectAddedEvent)
        wrapped_self.currentlySubscribed = True
    subscribe = ContextMethod(subscribe)

    def unsubscribe(wrapped_self):
        if not wrapped_self.currentlySubscribed:
            raise RuntimeError, "not subscribed; please subscribe first"
        channel = wrapped_self._getChannel(None)
        channel.unsubscribe(wrapped_self, IObjectAddedEvent)
        wrapped_self.currentlySubscribed = False
    unsubscribe = ContextMethod(unsubscribe)

    def isSubscribed(self):
        return self.currentlySubscribed

    def registerExisting(wrapped_self):
        object = findContentObject(wrapped_self)
        hub = getService(wrapped_self, "ObjectHub")
        wrapped_self._registerTree(object, hub)
    registerExisting = ContextMethod(registerExisting)

    def _registerTree(wrapped_self, object, hub):
        wrapped_self._registerObject(object, hub)
        # XXX Policy decision: only traverse into folders
        if not IFolder.isImplementedBy(object):
            return
        # Register subobjects
        names = object.keys()
        for name in names:
            # XXX Once traverseName is refactored, should get an
            #     ITraversable from object and pass it to traverseName
            sub_object = traverseName(object, name)
            wrapped_self._registerTree(sub_object, hub)
    _registerTree = ContextMethod(_registerTree)

    def _registerObject(wrapped_self, object, hub):
        # XXX Policy decision: register absolutely everything
        try:
            hub.register(object)
        except ObjectHubError:
            # Already registered
            pass
    _registerObject = ContextMethod(_registerObject)

    def _getChannel(wrapped_self, channel):
        if channel is None:
            channel = getService(wrapped_self, "ObjectHub")
        return channel
    _getChannel = ContextMethod(_getChannel)

def findContentObject(context):
    # We want to find the (content) Folder in whose service manager we
    # live.  There are man y way to do this.  Perhaps the simplest is
    # looking for '++etc++Services' in the location.  We could also
    # walk up the path looking for something that implements IFolder;
    # the service manager and packages don't implement this.  Or
    # (perhaps better, because a service manager might be attached to
    # a non-folder container) assume we're in service space, and walk
    # up until we find a service manager, and then go up one more
    # step.  Walking up the path could be done by stripping components
    # from the end of the path one at a time and doing a lookup each
    # time, or more directly by traversing the context.  Traversing
    # the context can be done by getting the context and following the
    # chain back; there's a convenience class, ContainmentIterator to
    # do that.  Use the version of ContainmentIterator from
    # Zope.Proxy, which is aware of the complications caused by
    # security proxies.

    # For now, we pick the first approach.
    location = getPhysicalPath(context)
    # Location is a tuple of strings, starting with '' (for the root)
    for i in range(len(location)):
        if location[i] == "++etc++Services":
            location = location[:i]
            break
    else:
        raise ValueError, "can't find '++etc++Services' in path"
    root = getPhysicalRoot(context)
    return traverse(root, location)


=== Added File Zope3/src/zope/app/index/subscription_control.pt ===
<html metal:use-macro="views/standard_macros/page">

  <head>
    <title>Registration "Service" Control Page</title>
  </head>

  <body>

  <div metal:fill-slot="body">

    <h1>Subscription control</h1>

    <span tal:condition="request/callSubscribe|nothing" tal:omit-tag="">
        <span tal:define="dummy context/subscribe" tal:omit-tag=""/>
        Successfully subscribed.
    </span>
    <span tal:condition="request/callUnsubscribe|nothing" tal:omit-tag="">
        <span tal:define="dummy context/unsubscribe" tal:omit-tag=""/>
        Successfully unsubscribed.
    </span>
    <span tal:condition="request/callRegisterExisting|nothing" tal:omit-tag="">
        <span tal:define="dummy context/registerExisting" tal:omit-tag=""/>
        Registration done.
    </span>

    <form method="POST">
       <span tal:condition="context/isSubscribed" tal:omit-tag="">
           Subscription state: ON
           <input type="submit" value="Unsubscribe" name="callUnsubscribe" />
       </span>
       <span tal:condition="not:context/isSubscribed" tal:omit-tag="">
           Subscription state: OFF
           <input type="submit" value="Subscribe" name="callSubscribe" />
       </span>
    </form>

    <form method="POST">
        <input type="submit" value="Register Existing Objects"
               name="callRegisterExisting" />
    </form>

  </div>

  </body>

</html>