[Checkins] SVN: bluebream/website/docs/v1.0/manual/componentarchitecture.rst Added ZCA chapter

Baiju M baiju.m.mail at gmail.com
Sun Jan 24 02:12:17 EST 2010


Log message for revision 108420:
  Added ZCA chapter
  

Changed:
  A   bluebream/website/docs/v1.0/manual/componentarchitecture.rst

-=-
Added: bluebream/website/docs/v1.0/manual/componentarchitecture.rst
===================================================================
--- bluebream/website/docs/v1.0/manual/componentarchitecture.rst	                        (rev 0)
+++ bluebream/website/docs/v1.0/manual/componentarchitecture.rst	2010-01-24 07:12:17 UTC (rev 108420)
@@ -0,0 +1,411 @@
+Zope Component Architecture
+===========================
+
+Introdction
+-----------
+
+`Zope Component Architecture (ZCA)` is a framework for supporting
+component based design and programming.  It is very well suited to
+developing large Python software systems.  The ZCA is not specific to
+the Zope web application server: it can be used for developing any
+Python application.
+
+The ZCA is all about using Python objects effectively.  Components
+are reusable objects with introspectable interfaces.  A component
+provides an interface implemented in a class, or any other callable
+object.  It doesn't matter how the component is implemented, the
+important part is that it comply with its interface contracts.  Using
+ZCA, you can spread the complexity of systems over multiple
+cooperating components.  It helps you to create two basic kinds of
+components: `adapter` and `utility`.
+
+There are two core packages related to the ZCA:
+
+* `zope.interface` is used to define the interface of a component.
+
+* `zope.component` deals with registration and retrieval of
+  components.
+
+Remember, the ZCA is not about the components themselves, rather it
+is about creating, registering, and retrieving components.  Remember
+also, an `adapter` is a normal Python class (or a factory in general)
+and `utility` is a normal Python callable object.
+
+The ZCA framework is developed as part of the Zope 3 project.  As
+noted earlier, it is a pure Python framework, so it can be used in any
+kind of Python application.  Currently both Zope 3 and Zope 2 projects
+use this framework extensively.  There are many other projects
+including non-web applications using it.
+
+Installation
+------------
+
+Using `zc.buildout` with `zc.recipe.egg` recipe you can create Python
+interpreter with specified Python eggs.  First you can create a
+directory and initialize Buildout::
+
+  $ mkdir explore-zope.component
+  $ cd explore-zope.component
+  $ echo "#Buildout configuration" > buildout.cfg
+  $ svn co svn://svn.zope.org/repos/main/zc.buildout/trunk/bootstrap
+  $ ~/usr/bin/python2.4 bootstrap/bootstrap.py
+
+Now modify the `buildout.cfg` like this::
+
+  [buildout]
+  parts = py
+
+  [py]
+  recipe = zc.recipe.egg
+  eggs = zope.component
+  interpreter = mypython
+
+Now run `buildout` script inside `bin` directory.  This will download
+zope.component and its dependency eggs and install it.  Now you can
+access the interpreter created by the Buildout recipe like this::
+
+  $ ./bin/buildout
+  $ ./bin/mypython
+  >>> import zope.component
+
+Adapters
+--------
+
+
+Implementation
+~~~~~~~~~~~~~~
+
+This section will describe adapters in detail.  Zope component
+architecture, as you noted, helps to effectively use Python objects.
+Adapter components are one of the basic components used by Zope
+component architecture for effectively using Python objects.  Adapter
+components are Python objects, but with well defined interface.
+
+To declare a class is an adapter use `adapts` function defined in
+`zope.component` package.  Here is a new `FrontDeskNG` adapter with
+explicit interface declaration::
+
+  >>> from zope.interface import implements
+  >>> from zope.component import adapts
+
+  >>> class FrontDeskNG(object):
+  ...
+  ...     implements(IDesk)
+  ...     adapts(IGuest)
+  ...
+  ...     def __init__(self, guest):
+  ...         self.guest = guest
+  ...
+  ...     def register(self):
+  ...         guest = self.guest
+  ...         next_id = get_next_id()
+  ...         bookings_db[next_id] = {
+  ...         'name': guest.name,
+  ...         'place': guest.place,
+  ...         'phone': guest.phone
+  ...         }
+
+What you defined here is an `adapter` for `IDesk`, which adapts
+`IGuest` object.  The `IDesk` interface is implemented by
+`FrontDeskNG` class.  So, an instance of this class will provide
+`IDesk` interface.
+
+::
+
+  >>> class Guest(object):
+  ...
+  ...     implements(IGuest)
+  ...
+  ...     def __init__(self, name, place):
+  ...         self.name = name
+  ...         self.place = place
+
+  >>> jack = Guest("Jack", "Bangalore")
+  >>> jack_frontdesk = FrontDeskNG(jack)
+
+  >>> IDesk.providedBy(jack_frontdesk)
+  True
+
+The `FrontDeskNG` is just one adapter you created, you can also
+create other adapters which handles guest registration differently.
+
+
+Registration
+~~~~~~~~~~~~
+
+To use this adapter component, you have to register this in a
+component registry also known as site manager.  A site manager
+normally resides in a site.  A site and site manager will be more
+important when developing a Zope 3 application.  For now you only
+required to bother about global site and global site manager ( or
+component registry).  A global site manager will be in memory, but a
+local site manager is persistent.
+
+To register your component, first get the global site manager::
+
+  >>> from zope.component import getGlobalSiteManager
+  >>> gsm = getGlobalSiteManager()
+  >>> gsm.registerAdapter(FrontDeskNG,
+  ...                     (IGuest,), IDesk, 'ng')
+
+To get the global site manager, you have to call
+`getGlobalSiteManager` function available in `zope.component`
+package.  In fact, the global site manager is available as an
+attribute (`globalSiteManager`) of `zope.component` package.  So, you
+can directly use `zope.component.globalSiteManager` attribute.  To
+register the adapter in component, as you can see above, use
+`registerAdapter` method of component registry.  The first argument
+should be your adapter class/factory.  The second argument is a tuple
+of `adaptee` objects, i.e, the object which you are adapting.  In
+this example, you are adapting only `IGuest` object.  The third
+argument is the interface implemented by the adapter component.  The
+fourth argument is optional, that is the name of the particular
+adapter.  Since you gave a name for this adapter, this is a `named
+adapter`.  If name is not given, it will default to an empty string
+('').
+
+In the above registration, you have given the adaptee interface and
+interface to be provided by the adapter.  Since you have already
+given these details in adapter implementation, it is not required to
+specify again.  In fact, you could have done the registration like
+this::
+
+  >>> gsm.registerAdapter(FrontDeskNG, name='ng')
+
+There are some old API to do the registration, which you should
+avoid.  The old API functions starts with `provide`, eg:
+`provideAdapter`, `provideUtility` etc.  While developing a Zope 3
+application you can use Zope configuration markup language (ZCML) for
+registration of components.  In Zope 3, local components (persistent
+components) can be registered from Zope Management Interface (ZMI) or
+you can do it programmatically also.
+
+You registered `FrontDeskNG` with a name `ng`.  Similarly you can
+register other adapters with different names.  If a component is
+registered without name, it will default to an empty string.
+
+
+Querying adapter
+~~~~~~~~~~~~~~~~
+
+Retrieving registered components from component registry is achieved
+through two functions available in `zope.component` package.  One of
+them is `getAdapter` and the other is `queryAdapter`.  Both functions
+accepts same arguments.  The `getAdapter` will raise
+`ComponentLookupError` if component lookup fails on the other hand
+queryAdapter will return `None`.
+
+You can import the methods like this::
+
+  >>> from zope.component import getAdapter
+  >>> from zope.component import queryAdapter
+
+In the previous section you have registered a component for guest
+object (adaptee) which provides `IDesk` interface with name as `ng`.
+In the first section of this chapter, you have created a guest object
+named `jack`.
+
+This is how you can retrieve a component which adapts the interface
+of jack object (`IGuest`) and provides `IDesk` interface also
+with name as `ng`.  Here both `getAdapter` and
+`queryAdapter` works similarly::
+
+  >>> getAdapter(jack, IDesk, 'ng') #doctest: +ELLIPSIS
+  <FrontDeskNG object at ...>
+  >>> queryAdapter(jack, IDesk, 'ng') #doctest: +ELLIPSIS
+  <FrontDeskNG object at ...>
+
+As you can see, the first argument should be adaptee then, the
+interface which should be provided by component and last the name of
+adapter component.
+
+If you try to lookup the component with an name not used for
+registration but for same adaptee and interface, the lookup will fail.
+Here is how the two methods works in such a case::
+
+  >>> getAdapter(jack, IDesk, 'not-exists') #doctest: +ELLIPSIS
+  Traceback (most recent call last):
+  ...
+  ComponentLookupError: ...
+  >>> reg = queryAdapter(jack,
+  ...           IDesk, 'not-exists') #doctest: +ELLIPSIS
+  >>> reg is None
+  True
+
+As you can see above, `getAdapter` raised a
+`ComponentLookupError` exception, but `queryAdapter`
+returned `None` when lookup failed.
+
+The third argument, the name of registration, is optional.  If the
+third argument is not given it will default to empty string ('').
+Since there is no component registered with an empty string,
+`getAdapter` will raise `ComponentLookupError`.  Similarly
+`queryAdapter` will return `None`, see yourself how it
+works::
+
+  >>> getAdapter(jack, IDesk) #doctest: +ELLIPSIS
+  Traceback (most recent call last):
+  ...
+  ComponentLookupError: ...
+  >>> reg = queryAdapter(jack, IDesk) #doctest: +ELLIPSIS
+  >>> reg is None
+  True
+
+In this section you have learned how to register a simple adapter and
+how to retrieve it from component registry.  These kind of adapters is
+called single adapter, because it adapts only one adaptee.  If an
+adapter adapts more that one adaptee, then it is called multi adapter.
+
+
+Retrieving adapter using interface
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Adapters can be directly retrieved using interfaces, but it will only
+work for non-named single adapters.  The first argument is the adaptee
+and the second argument is a keyword argument.  If adapter lookup
+fails, second argument will be returned.
+
+::
+
+  >>> IDesk(jack, alternate='default-output')
+  'default-output'
+
+  Keyword name can be omitted:
+
+  >>> IDesk(jack, 'default-output')
+  'default-output'
+
+  If second argument is not given, it will raise `TypeError`:
+
+  >>> IDesk(jack) #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+  Traceback (most recent call last):
+  ...
+  TypeError: ('Could not adapt',
+    <Guest object at ...>,
+    <InterfaceClass __builtin__.IDesk>)
+
+  Here `FrontDeskNG` is registered without name:
+
+  >>> gsm.registerAdapter(FrontDeskNG)
+
+  Now the adapter lookup should succeed:
+
+  >>> IDesk(jack, 'default-output') #doctest: +ELLIPSIS
+  <FrontDeskNG object at ...>
+
+For simple cases, you may use interface to get adapter components.
+
+
+Utility
+-------
+
+Now you know the concept of interface, adapter and component registry.
+Sometimes it would be useful to register an object which is not
+adapting anything.  Database connection, XML parser, object returning
+unique Ids etc. are examples of these kinds of objects.  These kind of
+components provided by the ZCA are called `utility` components.
+
+Utilities are just objects that provide an interface and that are
+looked up by an interface and a name.  This approach creates a global
+registry by which instances can be registered and accessed by
+different parts of your application, with no need to pass the
+instances around as parameters.
+
+You need not to register all component instances like this.  Only
+register components which you want to make replaceable.
+
+
+Simple utility
+~~~~~~~~~~~~~~
+
+A utility can be registered with a name or without a name.  A utility
+registered with a name is called named utility, which you will see in
+the next section.  Before implementing the utility, as usual, define
+its interface.  Here is a `greeter` interface::
+
+  >>> from zope.interface import Interface
+  >>> from zope.interface import implements
+
+  >>> class IGreeter(Interface):
+  ...
+  ...     def greet(name):
+  ...         """Say hello"""
+
+Like an adapter a utility may have more than one implementation.  Here
+is a possible implementation of the above interface::
+
+  >>> class Greeter(object):
+  ...
+  ...     implements(IGreeter)
+  ...
+  ...     def greet(self, name):
+  ...         return "Hello " + name
+
+The actual utility will be an instance of this class.  To use this
+utility, you have to register it, later you can query it using the ZCA
+API.  You can register an instance of this class (`utility`) using
+`registerUtility`::
+
+  >>> from zope.component import getGlobalSiteManager
+  >>> gsm = getGlobalSiteManager()
+
+  >>> greet = Greeter()
+  >>> gsm.registerUtility(greet, IGreeter)
+
+In this example you registered the utility as providing the `IGreeter`
+interface.  You can look the interface up with either `queryUtility`
+or `getUtility`::
+
+  >>> from zope.component import queryUtility
+  >>> from zope.component import getUtility
+
+  >>> queryUtility(IGreeter).greet('Jack')
+  'Hello Jack'
+
+  >>> getUtility(IGreeter).greet('Jack')
+  'Hello Jack'
+
+As you can see, adapters are normally classes, but utilities are
+normally instances of classes.  Only once you are creating the
+instance of a utility class, but adapter instances are dynamically
+created whenever you query for it.
+
+
+Named utility
+~~~~~~~~~~~~~
+
+When registering a utility component, like adapter, you can use a
+name.  As mentioned in the previous section, a utility registered with
+a particular name is called named utility.
+
+This is how you can register the `greeter` utility with a name::
+
+  >>> greet = Greeter()
+  >>> gsm.registerUtility(greet, IGreeter, 'new')
+
+In this example you registered the utility with a name as providing
+the `IGreeter` interface.  You can look up the interface with either
+`queryUtility` or `getUtility`::
+
+  >>> from zope.component import queryUtility
+  >>> from zope.component import getUtility
+
+  >>> queryUtility(IGreeter, 'new').greet('Jill')
+  'Hello Jill'
+
+  >>> getUtility(IGreeter, 'new').greet('Jill')
+  'Hello Jill'
+
+As you can see here, while querying you have to use the `name` as
+second argument.
+
+Calling `getUtility` function without a name (second argument) is
+equivalent to calling with an empty string as the name.  Because, the
+default value for second (keyword) argument is an empty string.
+Then, component lookup mechanism will try to find the component with
+name as empty string, and it will fail.  When component lookup fails
+it will raise `ComponentLookupError` exception.  Remember, it will
+not return some random component registered with some other name.
+The adapter look up functions, `getAdapter` and `queryAdapter` also
+works similarly.
+



More information about the checkins mailing list