--- a/docs/source/zca.rst Thu Jan 11 16:57:23 2018 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,262 +0,0 @@
-.. _zca:
-
-Managing ZCA with PyAMS
-=======================
-
-The **Zope Component Architecture** (aka ZCA) is used by the Pyramid framework "under the hood" to handle interfaces,
-adapters and utilities. You don't **have to** use it in your own applications. But you can.
-
-The ZCA is mainly adding elements like **interfaces**, **adapters** and **utilities** to the Python language. It
-allows you to write a framework or an application by using **components** which can be extended easily.
-
-You will find several useful resources about ZCA concepts on the internet.
-
-
-Local utilities
----------------
-
-In ZCA, a **utility** is a **registered** component which provides an **interface**. This interface is the
-**contract** which defines features provided by the component which implements it.
-
-When a Pyramid application starts, a **global registry** is created to register a whole set of utilities and
-adapters; this registration can be done via ZCML directives or via native Python code.
-In addition, PyAMS allows you to define **local utilities**, which are stored and registered in the ZODB via a **site
-manager**.
-
-
-Defining site root
-------------------
-
-One of PyAMS pre-requisites is to use the ZODB, at least to store the site root application, it's configuration and a
-set of local utilities. :ref:`site` describes application startup and **local site manager**
-initialization process.
-
-This site can be used to store **local utilities** whose configuration, which is easily available to site
-administrators through management interface, is stored in the ZODB.
-
-
-Registering global utilities
-----------------------------
-
-**Global utilities** are components providing an interface which are registered in the global registry.
-PyAMS_utils package provides custom annotations to register global utilities without using ZCML. For example, a skin
-is nothing more than a simple utility providing the *ISkin* interface:
-
-.. code-block:: python
-
- from pyams_default_theme.layer import IPyAMSDefaultLayer
- from pyams_skin.interfaces import ISkin
- from pyams_utils.registry import utility_config
-
- @utility_config(name='PyAMS default skin', provides=ISkin)
- class PyAMSDefaultSkin(object):
- """PyAMS default skin"""
-
- label = _("PyAMS default skin")
- layer = IPyAMSDefaultLayer
-
-This annotation registers a utility, named *PyAMS default skin*, providing the *ISkin* interface. It's the developer
-responsibility to provide all attributes and methods required by the provided interface.
-
-
-Registering local utilities
----------------------------
-
-A local utility is a persistent object, registered in a *local site manager*, and providing a specific interface (if
-a component provides several interfaces, it can be registered several times).
-
-Some components can be required by a given package, and created automatically via the *pyams_upgrade* command line
-script; this process relies on the *ISiteGenerations* interface, for example for the timezone utility, a component
-provided by PyAMS_utils package to handle server timezone and display times correctly:
-
-.. code-block:: python
-
- from pyams_utils.interfaces.site import ISiteGenerations
- from pyams_utils.interfaces.timezone import IServerTimezone
-
- from persistent import Persistent
- from pyams_utils.registry import utility_config
- from pyams_utils.site import check_required_utilities
- from pyramid.events import subscriber
- from zope.container.contained import Contained
- from zope.interface import implementer
- from zope.schema.fieldproperty import FieldProperty
-
- @implementer(IServerTimezone)
- class ServerTimezoneUtility(Persistent, Contained):
-
- timezone = FieldProperty(IServerTimezone['timezone'])
-
- REQUIRED_UTILITIES = ((IServerTimezone, '', ServerTimezoneUtility, 'Server timezone'),)
-
- @subscriber(INewLocalSite)
- def handle_new_local_site(event):
- """Create a new ServerTimezoneUtility when a site is created"""
- site = event.manager.__parent__
- check_required_utilities(site, REQUIRED_UTILITIES)
-
- @utility_config(name='PyAMS timezone', provides=ISiteGenerations)
- class TimezoneGenerationsChecker(object):
- """Timezone generations checker"""
-
- generation = 1
-
- def evolve(self, site, current=None):
- """Check for required utilities"""
- check_required_utilities(site, REQUIRED_UTILITIES)
-
-Some utilities can also be created manually by an administrator through the management interface, and registered
-automatically after their creation. For example, this is how a ZEO connection utility (which is managing settings to
-define a ZEO connection) is registered:
-
-.. code-block:: python
-
- from pyams_utils.interfaces.site import IOptionalUtility
- from pyams_utils.interfaces.zeo import IZEOConnection
- from zope.annotation.interfaces import IAttributeAnnotatable
- from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectRemovedEvent
-
- from persistent import Persistent
- from pyramid.events import subscriber
- from zope.container.contained import Contained
-
- @implementer(IZEOConnection)
- class ZEOConnection(object):
- """ZEO connection object. See source code to get full implementation..."""
-
- @implementer(IOptionalUtility, IAttributeAnnotatable)
- class ZEOConnectionUtility(ZEOConnection, Persistent, Contained):
- """Persistent ZEO connection utility"""
-
- @subscriber(IObjectAddedEvent, context_selector=IZEOConnection)
- def handle_added_connection(event):
- """Register new ZEO connection when added"""
- manager = event.newParent
- manager.registerUtility(event.object, IZEOConnection, name=event.object.name)
-
- @subscriber(IObjectRemovedEvent, context_selector=IZEOConnection)
- def handle_removed_connection(event):
- """Un-register ZEO connection when deleted"""
- manager = event.oldParent
- manager.unregisterUtility(event.object, IZEOConnection, name=event.object.name)
-
-*context_selector* is a custom subscriber predicate, so that subscriber event is activated only if object concerned
-by an event is providing given interface.
-
-
-Looking for utilities
----------------------
-
-ZCA provides the *getUtility* and *queryUtility* functions to look for a utility. But these methods only applies to
-global registry.
-
-PyAMS package provides equivalent functions, which are looking for components into local registry before looking into
-the global one. For example:
-
-.. code-block:: python
-
- from pyams_security.interfaces import ISecurityManager
- from pyams_utils.registry import query_utility
-
- manager = query_utility(ISecurityManager)
- if manager is not None:
- print("Manager is there!")
-
-All ZCA utility functions have been ported to use local registry: *registered_utilities*, *query_utility*,
-*get_utility*, *get_utilities_for*, *get_all_utilities_registered_for* functions all follow the equivalent ZCA
-functions API, but are looking for utilities in the local registry before looking in the global registry.
-
-
-Registering adapters
---------------------
-
-An adapter is also a kind of utility. But instead of *just* providing an interface, it adapts an input object,
-providing a given interface, to provide another interface. An adapter can also be named, so that you can choose which
-adapter to use at a given time.
-
-PyAMS_utils provide another annotation, to help registering adapters without using ZCML files. An adapter can be a
-function which directly returns an object providing the requested interface, or an object which provides the interface.
-
-The first example is an adapter which adapts any persistent object to get it's associated transaction manager:
-
-.. code-block:: python
-
- from persistent.interfaces import IPersistent
- from transaction.interfaces import ITransactionManager
- from ZODB.interfaces import IConnection
-
- from pyams_utils.adapter import adapter_config
-
- @adapter_config(context=IPersistent, provides=ITransactionManager)
- def get_transaction_manager(obj):
- conn = IConnection(obj)
- try:
- return conn.transaction_manager
- except AttributeError:
- return conn._txn_mgr
-
-This is another adapter which adapts any contained object to the *IPathElements* interface; this interface can be
-used to build index that you can use to find objects based on a parent object:
-
-.. code-block:: python
-
- from pyams_utils.interfaces.traversing import IPathElements
- from zope.intid.interfaces import IIntIds
- from zope.location.interfaces import IContained
-
- from pyams_utils.adapter import ContextAdapter
- from pyams_utils.registry import query_utility
- from pyramid.location import lineage
-
- @adapter_config(context=IContained, provides=IPathElements)
- class PathElementsAdapter(ContextAdapter):
- """Contained object path elements adapter"""
-
- @property
- def parents(self):
- intids = query_utility(IIntIds)
- if intids is None:
- return []
- return [intids.register(parent) for parent in lineage(self.context)]
-
-An adapter can also be a multi-adapter, when several input objects are requested to provide a given interface. For
-example, many adapters require a context and a request, eventually a view, to provide another feature. This is how,
-for example, we define a custom *name* column in a security manager table displaying a list of plug-ins:
-
-.. code-block:: python
-
- from pyams_zmi.layer import IAdminLayer
- from z3c.table.interfaces import IColumn
-
- from pyams_skin.table import I18nColumn
- from z3c.table.column import GetAttrColumn
-
- @adapter_config(name='name', context=(Interface, IAdminLayer, SecurityManagerPluginsTable), provides=IColumn)
- class SecurityManagerPluginsNameColumn(I18nColumn, GetAttrColumn):
- """Security manager plugins name column"""
-
- _header = _("Name")
- attrName = 'title'
- weight = 10
-
-
-Registering vocabularies
-------------------------
-
-A **vocabulary** is a custom factory which can be used as source for several field types, like *choices* or *lists*.
-Vocabularies have to be registered in a custom registry, so PyAMS_utils provide another annotation to register them.
-This example is based on the *Timezone* component which allows you to select a timezone between a list of references:
-
-.. code-block:: python
-
- import pytz
- from pyams_utils.vocabulary import vocabulary_config
- from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
-
- @vocabulary_config(name='PyAMS timezones')
- class TimezonesVocabulary(SimpleVocabulary):
- """Timezones vocabulary"""
-
- def __init__(self, *args, **kw):
- terms = [SimpleTerm(t, t, t) for t in pytz.all_timezones]
- super(TimezonesVocabulary, self).__init__(terms)