Local Site Manager
==================

We start from an empty folder.  Since ``OFS.Folder``'s extend
``ObjectSiteManager`` they all get to be ``IPossibleSite``'s.

    >>> from OFS.Folder import Folder
    >>> site = Folder('site')

Of course we now need to transform that IPossibleSite into a real ISite.

    >>> import zope.component
    >>> from zope.app.component.hooks import setSite as setActiveSite

    >>> from five.localsitemanager import make_objectmanager_site
    >>> make_objectmanager_site(site)
    >>> sitemanager = site.getSiteManager()
    >>> sitemanager
    <PersistentComponents ...>

Make sure this site has it's ``__bases__`` configure appropriately.

    >>> sitemanager.__bases__
    (<BaseGlobalComponents ...>,)

Utilities
---------

Utilities can now be registered with our site manager.  We can confirm this
by setting up a test utility.

    >>> from OFS.SimpleItem import SimpleItem
    >>> from zope import interface

    >>> class ITestUtility(interface.Interface): pass
    >>> class TestUtility(object):
    ...     interface.implements(ITestUtility)
    ...     def __init__(self, id):
    ...         self.id = id
    ...     def __repr__(self):
    ...         return '<Utility %s "%s">' % (self.__class__.__name__, self.id)

    >>> sitemanager.registerUtility(TestUtility('test'),
    ...                             name=u'hello_world',
    ...                             provided=ITestUtility)
    >>> sitemanager.getUtility(ITestUtility, name=u'hello_world')
    <Utility TestUtility "test">

Make sure the utility lookup only works when the correct active site has
been configured.

    >>> setActiveSite(None)
    >>> zope.component.queryUtility(ITestUtility, name=u'hello_world') is None
    True

    >>> setActiveSite(site)
    >>> zope.component.queryUtility(ITestUtility, name=u'hello_world')
    <Utility TestUtility "test">

Adapters
---------

Adapters can now be registered with our site manager.  We can confirm this
by setting up a test adapter.

    >>> from OFS.SimpleItem import SimpleItem
    >>> from zope import interface

    >>> class IFoo(interface.Interface): pass
    >>> class Foo(object):
    ...     interface.implements(IFoo)
    ...     def __init__(self, id):
    ...         self.id = id

    >>> class ITestAdapter(interface.Interface): pass
    >>> class TestAdapter(object):
    ...     interface.implements(ITestAdapter)
    ...     def __init__(self, context):
    ...         self.context = context
    ...     def __repr__(self):
    ...         return '<Adapter %s adapting "%s">' % (self.__class__.__name__,
    ...                                                self.context.id)

    >>> sitemanager.registerAdapter(TestAdapter,
    ...                             required=(IFoo,),
    ...                             provided=ITestAdapter)
    >>> sitemanager.getAdapter(Foo('foo'), ITestAdapter)
    <Adapter TestAdapter adapting "foo">

Make sure the adapter lookup only works when the correct active site has
been configured.

    >>> setActiveSite(None)
    >>> zope.component.queryAdapter(Foo('foo'), ITestAdapter) is None
    True

    >>> setActiveSite(site)
    >>> zope.component.queryAdapter(Foo('foo'), ITestAdapter)
    <Adapter TestAdapter adapting "foo">
    >>> ITestAdapter(Foo('foo'))
    <Adapter TestAdapter adapting "foo">

Acquisition
-----------

Now to mix a little required Zope 2 confusion into everything, we must ensure
that the aq chain is predictable.  And based on consensus we decided that
the acquired parent of a returned utility should be the ``ISite`` that
owns the ``ISiteManager`` that returned the utility.  We need to ensure
all the ways of getting a utility have been covered.  Of course this should
only happen if the utility is acquisition aware to beging with.

    >>> import Acquisition
    >>> from Acquisition.interfaces import IAcquirer

First off, our utility isn't aq-wrapped so asking it what is aq_parent is
should return None.
 
    >>> comp = sitemanager.queryUtility(ITestUtility, name=u'hello_world')
    >>> Acquisition.aq_parent(comp) is None
    True

So now we setup a utility that is aq aware.

    >>> class AQTestUtility(Acquisition.Explicit, TestUtility): pass
    >>> sitemanager.registerUtility(AQTestUtility('test'),
    ...                             name=u'aq_wrapped',
    ...                             provided=ITestUtility)

And of course the aq parents should be the site now.

    >>> comp = sitemanager.getUtility(ITestUtility, name=u'aq_wrapped')
    >>> Acquisition.aq_parent(comp) is site
    True

And just to mix things up a bit.  Getting back multiple utilities should
allow us to test both aq and non-aq based components.

We start with getUtilitiesFor():

    >>> utils = [x for x in sitemanager.getUtilitiesFor(ITestUtility)]
    >>> len(utils)
    2

    >>> nonaqutils = [(name, comp)
    ...               for name, comp in utils if not IAcquirer.providedBy(comp)]
    >>> len(nonaqutils)
    1
    >>> name, comp = nonaqutils[0]
    >>> Acquisition.aq_parent(comp) is None
    True

    >>> aqutils = [(name, comp)
    ...            for name, comp in utils if IAcquirer.providedBy(comp)]
    >>> len(aqutils)
    1
    >>> name, comp = aqutils[0]
    >>> Acquisition.aq_parent(comp) is site
    True

And then getAllUtilitiesRegisteredFor():

    >>> utils = [x for x in sitemanager.getAllUtilitiesRegisteredFor(ITestUtility)]
    >>> len(utils)
    2

    >>> nonaqutils = [comp for comp in utils if not IAcquirer.providedBy(comp)]
    >>> len(nonaqutils)
    1
    >>> comp = nonaqutils[0]
    >>> Acquisition.aq_parent(comp) is None
    True

    >>> aqutils = [comp for comp in utils if IAcquirer.providedBy(comp)]
    >>> len(aqutils)
    1
    >>> comp = aqutils[0]
    >>> Acquisition.aq_parent(comp) is site
    True

Nested Sites
------------

Whenever a component is queried using the component registry, the active
component registry (ie site manager) needs to be smart enough to check
all *parent* component registries as well.

Implementation-wise this means that each component registry needs to have
an appropriate ``__bases__`` attribute configured that is aware of
containment and (in the case of Zope 2) acquisition to some respect.

Start by creating some nested sites.

    >>> from five.localsitemanager import update_sitemanager_bases

    >>> folder1 = Folder('folder1')
    >>> make_objectmanager_site(folder1)
    >>> update_sitemanager_bases(folder1)

    >>> folder1_1 = Folder('folder1_1')
    >>> make_objectmanager_site(folder1_1)
    >>> ignored = folder1._setObject('folder1_1', folder1_1)
    >>> folder1_1 = folder1['folder1_1']
    >>> update_sitemanager_bases(folder1_1)

Now we check the actual next-site-lookup logic to make sure it's working.

    >>> from five.localsitemanager import find_next_sitemanager, get_parent

Needed to implement our own get_parent (zope3 has one) that is acquisition
aware.

    >>> get_parent(folder1)
    Traceback (most recent call last):
      ...
    TypeError: ('Not enough context...

    >>> get_parent(folder1_1)
    <Folder at folder1>

Any logic that sets up a site manager's ``__bases__`` will use the
``find_next_sitemanager`` function to figure out the next closest place
to look.

    >>> find_next_sitemanager(folder1) is None
    True

    >>> find_next_sitemanager(folder1_1)
    <PersistentComponents ...>

Now we make sure that that the ``__bases__`` have been setup appropriately.

    >>> folder1.getSiteManager().__bases__
    (<BaseGlobalComponents base>,)

    >>> folder1_1.getSiteManager().__bases__
    (<PersistentComponents ...>,)
