From jim at zope.com Tue Oct 16 11:38:13 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture Docs/ZopeComponentArchitecture - New directory
Message-ID: <200110161538.f9GFcDk28026@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture
In directory cvs.zope.org:/tmp/cvs-serv28004/ZopeComponentArchitecture
Log Message:
Directory /cvs-repository/Docs/ZopeComponentArchitecture added to the repository
=== Added directory Docs/ZopeComponentArchitecture ===
From jim at zope.com Wed Oct 24 09:31:52 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial Docs/ZopeComponentArchitecture/PythonProgrammerTutorial - New directory
Message-ID: <200110241331.f9ODVql11956@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial
In directory cvs.zope.org:/tmp/cvs-serv11950/PythonProgrammerTutorial
Log Message:
Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial added to the repository
=== Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial ===
From jim at zope.com Wed Oct 24 09:32:06 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 - New directory
Message-ID: <200110241332.f9ODW6b12020@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1
In directory cvs.zope.org:/tmp/cvs-serv12014/Chapter1
Log Message:
Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 added to the repository
=== Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 ===
From jim at zope.com Wed Oct 24 09:33:17 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - New directory
Message-ID: <200110241333.f9ODXH112851@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5
In directory cvs.zope.org:/tmp/cvs-serv12844/Step5
Log Message:
Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 added to the repository
=== Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 ===
From jim at zope.com Wed Oct 24 09:33:18 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 - New directory
Message-ID: <200110241333.f9ODXIL12865@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4
In directory cvs.zope.org:/tmp/cvs-serv12858/Step4
Log Message:
Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 added to the repository
=== Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 ===
From jim at zope.com Wed Oct 24 09:33:19 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 - New directory
Message-ID: <200110241333.f9ODXJu12879@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3
In directory cvs.zope.org:/tmp/cvs-serv12872/Step3
Log Message:
Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 added to the repository
=== Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 ===
From jim at zope.com Wed Oct 24 09:33:19 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 - New directory
Message-ID: <200110241333.f9ODXJY12893@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2
In directory cvs.zope.org:/tmp/cvs-serv12886/Step2
Log Message:
Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 added to the repository
=== Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 ===
From jim at zope.com Wed Oct 24 09:33:20 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 - New directory
Message-ID: <200110241333.f9ODXKE12907@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1
In directory cvs.zope.org:/tmp/cvs-serv12900/Step1
Log Message:
Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 added to the repository
=== Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 ===
From jim at zope.com Wed Oct 24 09:41:56 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial - README.txt:1.1
Message-ID: <200110241341.f9ODfux14450@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial
In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial
Added Files:
README.txt
Log Message:
work in progress. still pretty rough
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/README.txt ===
Zope Component Architecture Tutorial for Python Programmers
This tutorial presents the Zope Component Architecture to Python
programmers in a step-by-step form. The tutorial is arranged into
chapters, with a directory for each chapter. Each chapter is written
around an evolving sample implementation. Each topic in a chapter is
presented, in a chapter subdirectory, as an evolution of some sample
code. The sample directories are actual working Python packages
that can be installed into Zope 3 product directories.
The Zope component architecture has several goals, the most
important of which are:
- Make it much easier to create objects that are usable in Zope:
o Make it possible to use existing Python classes (with little or
no change).
o Utilize features of the Zope framework with small incremental
changes.
- Make it much easier to reuse objects in Zope.
o Use existing non-Zope-specific objects
o Add, remove, and replace functionality of existing objects.
o Change the user interface of existing objects,
o Provide alternative access methods (e.g. FTP, XML-RPC,
wxWindows) to objects.
Additional goals include:
- Avoid namespace issues
o Don't worry about conflicts with content names
o Don't worry about a large number of names
- Make it easy to discover content and application functionality.
- Make it easier to distribute solutions.
- Provide consistency between Zope and the CMF.
This tutorial will demonstrate how these goals are achived by the
component architecture.
Chapters:
Chapter1, Basic content types and components
Chapter2, Containers
Chapter3, Services, offers, customized traversal.
Chapter4, Converting traditional Zope products to component-based
products.
From jim at zope.com Wed Oct 24 09:41:56 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 - README.txt:1.1
Message-ID: <200110241341.f9ODfuC14457@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1
In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1
Added Files:
README.txt
Log Message:
work in progress. still pretty rough
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/README.txt ===
Chapter1, Basic content types and components
This chapter provides an introduction to the Zope component
architecture through a simple content-type example. A content
component is developed for storing simple personal contact
information. We begin by showing the minimum necessary to create
content objects in Zope, and then we show, gradually how
functionality can be added with components.
The presentation of the example proceeds in several steps:
Step1, minimal contact class
Step2, security asertions to make contact objects usable
Step3, a simple contact presentation
Step4, providing editing capabilities
Step5, adding features and utilities
Step6, providing a custom creation form
Summary
This chapter showed how to develop basic components with Python
modules. The facilities demonstrated address a number of the
component architecture goals:
- Make it much easier to create objects that are usable in Zope:
o Make it possible to use existing Python classes (with little or
no change).
We were able to use extremely simple classes. We needed to mix
'Persistent' into the content component, but this resriction
will be relaxed in the future. We didn't need to mix in any
special base classes in other components, although we chose to
use acquisition in some cases.
o Utilize features of the Zope framework with small incremental
changes.
Observe that as we progressed through the chapter, new modules
were added, but existing modules, other than the
configueration module remained unchanged.
The incremental improvements could as easily have been
provided in other packages by different authors.
- Make it much easier to reuse objects in Zope.
o Use existing non-Zope-specific objects
See above.
o Add, remove, and replace functionality of existing objects.
This only requires changes to the configuration
module(s). Configuration could be easily split over many
packages and, though not mentioned here, could be performed
through teh web. The ability to offer, rather than provide
interfaces provides flexibility in cases where alternative
implementations are expected.
o Change the user interface of existing objects,
This was not addressed directly, but it is as simple as simple
as changing which presentations are registered.
o Provide alternative access methods (e.g. FTP, XML-RPC,
wxWindows) to objects.
We'll see in later chapters how to add protocol support by
adding presentation objects.
Additional goals include:
- Avoid namespace issues
o Don't worry about conflicts with content names
It should be aparent that, with the Zope component
architecture, software components are accessed through API
calls, which use services. Components aren't accessed in the
same namespace as content.
o Don't worry about a large number of names
The separation of content from software, and the
classification of software into separate component types
(e.g. utilities and features) reduces the opportunity for
namespace conflicts. Note that the names in the component
namespaces typically have multiple parts, which are often
interfaces, rather than strings.
- Make it easy to discover content and application functionality.
This was not discussed in this chapter, however, it should be
obvious that, given an object, we should be able to find the
presentations and features that extend it based on it's
interaces. We'll show how this works in later chapters.
- Make it easier to distribute solutions.
This goal will be addressed in later chapters.
- Provide consistency between Zope and the CMF.
This goal will be addressed in later chapters.
From jim at zope.com Wed Oct 24 09:41:57 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 - Contact.py:1.1 README.txt:1.1 __init__.py:1.1
Message-ID: <200110241341.f9ODfvn14470@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1
In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1/Step1
Added Files:
Contact.py README.txt __init__.py
Log Message:
work in progress. still pretty rough
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/Contact.py ===
import Persistence
class Contact (Persistence.Persistent):
"""Contacts keep track of personal data, such as name, email
and postal address. All methods are protected."""
def __init__(self, first='', last='', email='', address='', pc=''):
self.update(first, last, email, address, pc)
def name(self):
return "%s %s" % (self._first, self._last)
def first(self):
return self._first
def last(self):
return self._last
def email(self):
return self._email
def address(self):
return self._address
def postal_code(self):
return self._pc
def update(self, first=None, last=None, email=None, address=None, pc=None):
if first is not None:
self._first = first
if last is not None:
self._last = last
if email is not None:
self._email = email
if address is not None:
self._address = address
if pc is not None:
self._pc = pc
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/README.txt ===
Step1, minimal contact class
In this step, we present the minimum steps necessary to get a class
usable in Zope.
The file 'Contact.py' contains the basic class definition. This
class doesn't use *any* Zope-specific classes. The class provides
methods for accessing contact data and for modifying the data.
This class mixes in the 'Persistent' base class. Doing so allows the
class to be stored in Zope's object database and causes changes to
the object to be saved automatically [1]. This is important because
we want class instances to be saved and managed in Zope. If
instances weren't going to ve stored in Zope, we would not need to
mix in persistence.
To use the contact class in Zope, we have to tell Zope about it. The
file, '__init__.py' is where we do this. Components are contained
in Zope file-based products [2], which are simply Python packages
that are sub-packages of the Zope 'Products' package. The
'__init__' module is responsible for initializing the package and
for getting necessary registrations done.
In this example, the '__init__' module calls the 'App.provideClass'
function to register the class. The class being regstered is passed
as the first argument. A keyword argument is used to specify a Zope
permission needed for creating instances. If a permission wasn't
specified, then contacts could not be created through the web or
through code managed through the web. Additional keyword arguments
can be used to provide additional meta data, however, the meta-data
is infered from class meta-data.
The 'App.provideClass' accomplishes two things:
1. A factory component is created and registered using the
class. This allows other application code to create instances::
contact=ComponentArchitecture.createObject(
ob, 'Products.Contact.Contact.Contact')
A identifier for the factory is generated from the registered
class module and class name. In this case, the module is
'Products.Contact.Contact' and the class is 'Contact'. A
different identifier could have been provided in the
'provideClass' call.
2. The factory is registered with Zope so that it can be included in
Zope's interface for adding objects to containers.
With this registration, contacts can be added to Zope folders.
Simply adding contacts to Zope folders is of limited usefulness. We
can't do anything with the added contacts from the Zope management
interface or from through the web code, because we haven't provided
security assertions that make them accessable. Conceivably, we could
use contacts from trusted (file-based) code.
In the next step, we'll make security assertions for the class,
which will make the class a lot more useful to Zope.
------------------------------------------------------------------------------
[1] -- There are a few additional restrictions, sometimes refered to
as the "rules of persistence", that persistent class
authors should be aware of. Persistent objects should be picklable
with Python's standard pickle module. Subobjects should be immutable
or persistent, or special handling is needed. See the Zope object
database documentation.
In is likely that, in the future, it will be possible to store
objects in Zope without mixing in 'Persistent', however, it will be
necessary to make explicit calls when objects are modified so that
changes can be saved.
[2] -- Components can also be developed through the web and contained
in the Zope object database. This tutorial, however, only discussed
file-based products.
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/__init__.py ===
import App # Top-level package. It would be better if this was Zope.App
import Contact
ManageContacts = 'Manage Contacts'
# Register the contact class
App.provideClass(Contact.Contact, permission=ManageContacts)
From jim at zope.com Wed Oct 24 09:41:57 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 - Contact.py:1.1 README.txt:1.1 __init__.py:1.1 view.pt:1.1
Message-ID: <200110241341.f9ODfvL14484@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2
In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1/Step2
Added Files:
Contact.py README.txt __init__.py view.pt
Log Message:
work in progress. still pretty rough
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/Contact.py ===
import Persistence
class Contact (Persistence.Persistent):
"""Contacts keep track of personal data, such as name, email
and postal address. All methods are protected."""
def __init__(self, first='', last='', email='', address='', pc=''):
self.update(first, last, email, address, pc)
def name(self):
return "%s %s" % (self._first, self._last)
def first(self):
return self._first
def last(self):
return self._last
def email(self):
return self._email
def address(self):
return self._address
def postal_code(self):
return self._pc
def update(self, first=None, last=None, email=None, address=None, pc=None):
if first is not None:
self._first = first
if last is not None:
self._last = last
if email is not None:
self._email = email
if address is not None:
self._address = address
if pc is not None:
self._pc = pc
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/README.txt ===
Step2, security asertions to make contact objects usable
In this step, we'll make security assertions that make the 'Contact'
class we created in Step1 usable in Zope. The contact module,
'Contact.py' remains unchanged. We add a security assertion in the
'__init__' module by calling 'AccessControl.protectClass'. The class
to be protected and the desired Zope permission are passed as
arguments. We also provide keyword arguments to specify protected
methods, by name, and to specify that access to instances should be
protected by the permission.
We could have made security assertions in the class definition,
using Zope declarative security API. Doing so would have made the
class much more Zope-specific. In this case, we'd like to keep the
class Zope-independent, so we made the security assertions with
'protectClass'. The two approaches are equivalent. The
'protectClass' call actually modifies the classes passed in exactly
the same way that the declarative security API would have.
In this example we protected the methods for getting contact
information with the Zope 'View' permission. With this change, we
can write templates and scripts that access these methods.
The file 'view.pt' shows a sample page template for viewing
contacts. This simple template used the contact methods 'first',
'last', 'address' and 'email' to access contact data. The template
could be added to Zope as a Zope page Template. Typically, it would
be included in the folder containing the data, or in a higher
containing folder. For the template to work, the template must be
"applied to" the contact object. This is typically done by visiting
the contact with a URL like::
..../aContact/view.pt
When this happens the template variable, 'here' is bound to the
contact, so the path expression 'here/first' calls the 'first'
method on the contact.
There is a little bit of a problem here. The URL above will only
work if the page template can be acquired from the contact. This
works for traditional Zope objects because they mix in the
Acquisition.Implicit' base class. Implicit acquisition causes an
object's containers to be automatically searched for attributes not
found in the object. Implicit acquisition is quite powerful, but can
sometimes lead to unpleasent surprises.
In Zope 3, objects will still take advantage of acquisition, but in
much more explicit ways. Most objects will have a searchable
acquisition context, but the context will be searched explicitly.
Because the contact class doesn't mix in acquisition, we can't
automatically acquire the view template. We could change the class
to mix in 'Acquisition.Implicit', but this would make the class a
bit too Zope-specific. One way to get around this is to request
acquisition explicitly in the URL::
..../aContact/(acquire)view.pt
Another approach would be to provide custom software that allows
contacts to acquire specific objects in URLs. We'll discuss this
approach in a later chapter of the tutorial.
There's a deeper problem. We might want the view template to be
invoked when we name just the contact::
..../aContact
We need a way to specify this. In the next step in the tutorial,
we'll use the component architecture to provide a default
presentation for contacts.
In this example, the view template was put in the folder containing
the content. The content, and the software for displaying the
content are mixed up in the same folder (or folder
hierarchy). Mixing software and content in the same place makes
managing both more difficult. We could put the presentation in the
class (or in a Zope through-the-web Class), but this requires
changing the class implementation and mixes presentation and
data. Components provide another way to accomplish the same thing,
but in a more flexible and organized fashion.
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/__init__.py ===
import App
import Contact
import AccessControl, AccessControl.Permissions
# Protect contact view methods:
protectMethods(
ViewPermission,
Contact,
'name', 'first', 'last', 'email', 'address', 'postal_code'
)
# Register the contact factory
ManageContacts = 'Manage Contacts'
App.provideClass(Contact.Contact, permission=ManageContacts)
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/view.pt ===
Contact Information
Contact information
Name:
FirstLast
Address:
Mailing address here
Email
foo@bar.com
From jim at zope.com Wed Oct 24 09:41:57 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 - Contact.py:1.1 ContactInfo.py:1.1 README.txt:1.1 __init__.py:1.1 view.pt:1.1
Message-ID: <200110241341.f9ODfvn14497@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3
In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1/Step3
Added Files:
Contact.py ContactInfo.py README.txt __init__.py view.pt
Log Message:
work in progress. still pretty rough
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/Contact.py ===
import Persistence,
class Contact (Persistence.Persistent):
"""Contacts keep track of personal data, such as name, email
and postal address. All methods are protected."""
def __init__(self, first='', last='', email='', address='', pc=''):
self.update(first, last, email, address, pc)
def name(self):
return "%s %s" % (self._first, self._last)
def first(self):
return self._first
def last(self):
return self._last
def email(self):
return self._email
def address(self):
return self._address
def postal_code(self):
return self._pc
def update(self, first=None, last=None, email=None, address=None, pc=None):
if first is not None:
self._first = first
if last is not None:
self._last = last
if email is not None:
self._email = email
if address is not None:
self._address = address
if pc is not None:
self._pc = pc
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/ContactInfo.py ===
import Interface, Contact
class ContactInfo (Interface.Base):
"Provides access to basic contact information."
def first(): "Get the firt name"
def last(): "Get the last name"
def email(): "Get the electronic mail address"
def address(): "Get the postal address"
def postal_code(): "Get the postal code"
def name():
"""Gets the contact name.
The contact name is the first and last name together.
"""
ContactInfo.assertImplementedByInstancesOf(Contact.Contact)
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/README.txt ===
Step3, a simple contact presentation
In this step, we'll begin to introduce Zope components. We'll use
Zope components to provide a default Web presentation for contacts.
Before getting into details, we'll define some terms, "component",
"content component" and "presentation component".
The definition of "component" is very simple. A component is an
object with introspectable interfaces. Interfaces allow us to
separate object behavior from implementation [1]. An interface is an
abstract description of behavior. Many different implementations can
provide the same behavior. This is important for a component system
because it makes it easier to assemble components to provide
specific functionality. In Zope, components have interfaces defined
using the 'Interface' package [2].
Interfaces specify behavior. We'll often refer to an interface as a
"contract" or a "promise". A component "implements" ("realizes",
"satisfies", "provide") it's interfaces.
Interfaces group (or "classify") objects based on their
behavior. Objects that provide the same interface are similar and
can be substituted for one another. The Zope component model
interface to connect related components based on interfaces.
A "content component" is a component that manages specific
information (e.g. contact information). Most other components exist
to support contact components by providing presentations or
application functionality.
A "presentation component" is a component used to present other
components with a specific user interface mechanism (e.g. Web
browser or a graphical user interface) or protocol (e.g. FTP or
XML-RPC). Presentation components don't provide data or
functionality except as necessary to support the presentation. We
will often use the word "presentation" alone where the context is
clear, as in "contact view presentation".
In this step, we'll use the component architecture to cause the view
page template to be used when viewing a contact. To use the
component architecture, we need to be able to create contact
components. That is, contact instances must be components and must
have interfaces.
The file 'ContactInfo.py' defines the 'ContactInfo' interface, which
specifies the methods used to get contact information [3]. Note
that the method definitions in the interface don't include a 'self'
argument. The reason for this is that the 'self' argument is not
part of the external behavior, but, rather, part of the Python
implementation of the methods. A caller doesn't pass arguments to
these functions, so arguments shouldn't appear in the method
interfaces.
After the interfac is created, the 'assertImplementedByInstancesOf'
method is called to assert that contact instances implement the
interface. In this example, we asserted the class' contract outside
the class definition. We could have included the interface assertion
in the 'Contact' class definition. We'll see an example of this in a
later example. There are two advanages to specifying the interface
externally, as we have here:
- The interface assertion doesn't require modifying the class
source. This could be handy if the class source is maintained by
someone else or if the class is used in other application
environments that don't support interfaces.
- The 'assertImplementedByInstancesOf' performs minimal conformance
checks and raises errors if the class can be determined not to
implement the interface. [4]
Now that contacts are components, we can associate presentation
components with them.
Our immediate need is to provide a simple view presentation for
contacts. We normally create components using Python and interfaces,
as we did with 'Contact'. This presentation component is very
simple, consisting of a single page, expressed as a page template.
Page templates provide a convenience functon, 'SimplePresentation'
for creating a web presentation from a page template source file. We
use this function to create our view presentation in the '__init__'
module.
Once the presentation has been created, we need to register it. In
this example, we register it twice. First register it under the name
'view'. This allows us to access the presentation with URLs like::
...../aContact/(p)view
The string, '(p)' in the URL signifies that a presentation component
should be accessed.
We also register the presentation as the default presentation by
providing an empty string. When a URL like::
...../aContact
is used, the default presentation will be used to display the
object.
The presentation component is registered using the
'ComponentArchitecture.providePresentation' method. The method takes
four arguments:
- context interface,
- the presentation name,
- the presentation type, and
- a component creator.
The context interface and the presentation name are used to select a
presentation component for a given component. A presentation
component is only applied to an object that implements it's context
interface [5]. The presentation component will use the methods provided
in the context interface to get the information to be presented.
The presentation type is used to distinguish different kinds of
presentations, such as web browser and FTP
presentations. Presentation kinds are expressed as interfaces. In
this example, we've used the 'ZPublisher.Browser.BrowserPublish'
interface to indicate a web browser presentation.
Finally, we provide the component creator. This is not actually a
component, but, rather a callable object that creates the component
when called with a context. This is necesary because the
presentation needs the object being presented to do it's job. The
view presentation is actually not a presentation, but an object that
returns the presentation when called.
We;ve made a change to the security assertions for the class. Rather
than provide a specific method list, we now specify the
'ContactInfo' interface instead. If all of the methods in an
interface are protected the same way, then we can use the interface
and avoid maintaining a separate method list.
------------------------------------------------------------------------------
[1] -- Some would argue that abstract base classes could provide the
same capability. This is true, however, we think interfaces provide
a cleaner approach, for a number of reasons:
- Using abstract base classes increases the depth and
complexity of class hierarchies.
- Using abstract base classes imposes the restriction that
classes must satifsy all of the promises of their base
classes. We feel that this is overly restrictive. This
restriction limits opportunities for reuse and the
restriction is often not satisfied in practice.
- Abstract base classes make it harder to reason about (or
introspect) the behavior of an object, because implementation
classes are intermixed with specification classes.
- Separating interfaces into separate objects allows interfaces
to provide services without exposing the services in
instances. For example, Zope interfaces have methods for
instrospecting the interface meta-data. If the interfaces
were base classes, then the methods would appear as instance
methods, which would not make sense.
[2] -- The 'Interface' package provides specific objects and utilities
for defining interfaces for Python objects. We'll show a number of
examples of interface use in this tutorial. For more information,
see the "Zope Interfaces" documentation.
[3] -- There are a number of ways to create interfaces, but a simple
way is with the python 'class' statement. A 'class' statement'
normally creates classes, but it can also be used to create other
objects. In this case, because we specify 'Interface.Base' as a
base, an interface is created. An interface *is not* a class. An
interface is an object that specifies behavior (as opposed to a
class, which is an object that provides behavior).
[4] -- The 'assertImplementedByInstancesOf' does *not* verify that the
instances of the class conforms to the interface. Such a
verification requires additional semantic information that isn't in
the interface. Thye best we can do is check for gross errors, like
missing methods or mismatches arguments.
[5] -- The context interface can be the special Python object,
'None', in which case the presentation can be applied to any object,
howeverm it can't use any specific object methods (or attributes),
so such presentations are rarely useful.
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/__init__.py ===
import App
import Interface
import Products.PageTemplate
from ContactInfo import ContactInfo
from Contact import Contact
import AccessControl, AccessControl.Permissions
import ZPublisher.Browser
# Protect contact view methods via the info interface:
AccessControl.protectClass(
Contact, AccessControl.Permissions.view,
inteface=ContactInfo,
instance=1,
)
# Register the contact factory
ManageContacts = 'Manage Contacts'
App.provideClass(Contact.Contact, permission=ManageContacts)
# Create a simple view presentation
ViewPresentation = Products.PageTemplate.SimplePresentation(
'view.pt',
globals(),
permission = AccessControl.Permissions.view,
)
# Register this presentation under the name "view"
ComponentArchitecture.providePresentation(
ContactInfo,
'view',
ZPublisher.Browser.BrowserPublish,
ViewPresentation)
# Register this presentation as the default
ComponentArchitecture.providePresentation(
ContactInfo,
'',
ZPublisher.Browser.BrowserPublish,
ViewPresentation)
#JJJ Maybe default=1
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/view.pt ===
Contact Information
Contact information
Name:
FirstLast
Address:
Mailing address here
Email
foo@bar.com
From jim at zope.com Wed Oct 24 09:41:58 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 - Contact.py:1.1 ContactEdit.py:1.1 ContactEditPresentation.py:1.1 ContactInfo.py:1.1 README.txt:1.1 __init__.py:1.1 configure.py:1.1 edit.pt:1.1 permissions.py:1.1 view.pt:1.1
Message-ID: <200110241341.f9ODfwO14516@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4
In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1/Step4
Added Files:
Contact.py ContactEdit.py ContactEditPresentation.py
ContactInfo.py README.txt __init__.py configure.py edit.pt
permissions.py view.pt
Log Message:
work in progress. still pretty rough
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/Contact.py ===
import Persistence,
class Contact (Persistence.Persistent):
"""Contacts keep track of personal data, such as name, email
and postal address. All methods are protected."""
def __init__(self, first='', last='', email='', address='', pc=''):
self.update(first, last, email, address, pc)
def name(self):
return "%s %s" % (self._first, self._last)
def first(self):
return self._first
def last(self):
return self._last
def email(self):
return self._email
def address(self):
return self._address
def postal_code(self):
return self._pc
def update(self, first=None, last=None, email=None, address=None, pc=None):
if first is not None:
self._first = first
if last is not None:
self._last = last
if email is not None:
self._email = email
if address is not None:
self._address = address
if pc is not None:
self._pc = pc
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactEdit.py ===
from ContactInfo import ContactInfo
from Contact import Contact
class ContactEdit(ContactInfo):
"Provides the ability to change basic contact information."
def update(first, last, email, address, pc):
"""Modifies contact data
'None' values are ignored.
"""
ContactEdit.assertImplementedByInstancesOf(Contact)
#BBB might want to reverse sense of this
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactEditPresentation.py ===
from ZPublisher.Browser import BrowserPublish
from AccessControl import ClassSecurityInfo
from permissions import ManageContacts
from Products.PageTemplate import PresentationPageTemplateFile
from App.common import absolute_url
from AccessControl.ZopeGuards import guarded_getattr
import Acquisition
class ContactEditPresentationClass(Acquisition.Explicit):
"""Provide an interface for editing a contact
"""
__implements__ = BrowserPublish
# Create a SecurityInfo for this class
security = ClassSecurityInfo()
security.setDefaultAccess('deny')
# All presentation components should provide this.
def getContext(self):
return self.aq_parent
# Input form
security.declareProtected(ManageContacts, 'form')
form = PresentationPageTemplateFile('edit.pt', globals())
# action method
security.declareProtected(ManageContacts, 'action')
def action(self, first, last, email, address, pc):
"Edit a contact"
self.getContext().update(first, last, email, address, pc)
return self.form()
def browser_traverse(self, request, name):
return guarded_getattr(self, name)
def browser_default(self, request):
return self, ('form',)
Globals.InitializeClass(ContactEditPresentationClass)
# Create the singleton component
ContactEditPresentation = ContactEditPresentationClass()
# and a binder for it
ContactEditPresentation = Acquisition.binder(ContactEditPresentation)
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactInfo.py ===
import Interface, Contact
class ContactInfo (Interface.Base):
"Provides access to basic contact information."
def first(): "Get the firt name"
def last(): "Get the last name"
def email(): "Get the electronic mail address"
def address(): "Get the postal address"
def postal_code(): "Get the postal code"
def name():
"""Gets the contact name.
The contact name is the first and last name together.
"""
ContactInfo.assertImplementedBy(Contact.Contact)
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/README.txt ===
Step4, providing editing capabilities
In this example, we'll provide an example of a presentation
component with multiple methods. We'll create an editing interface
for contacts. The file 'ContactEditPresentation.py' provides the
definition for the edit presentation component.
The component is defined by the 'ContactEditPresentation' class.
The class asserts that it implements the 'BrowserPublish' interface by
defining the '__implements__' attribute. This example illustrates
how to assert interfaces in a class definition.
Presentation components have to track a context for the
presentation. There are two common approaches for doing this:
- A separate instance of the component is created for each
context. The context is passed to the class constructor and stored
in an instance variable.
- A single instance is used for all contexts and context information
is kept in an acquisition wrapper created for each context.
The contact edit presentation component takes the second approach,
which is why it mixes in 'Acquisition.Explicit'. It is common to
use a 'getContext' method to encapsulate the mechanism used for
tracking context. In this case, the 'getContext' method uses the
acquisition variable, 'aq_parent' to get the acquisition context.
The presentation component provides a web page that displays an
edit form. It implements this using a
'PresentationPageTemplateFile', with the source in the file
'edit.pt'.
The action of the form is "action", which is the 'action' method of
the component. The 'action' method applies the update and redisplays
the editing form.
The form and action are protected using Zope's declarative security
API. This example illustrates making security assertions inline in
the class. We needed to use the 'ManageContacts' permission that
was defined in the '__init__' module in Step3. We've factored this
permission into a separate 'permissions' module.
The last two methods in the class are defined by the 'BrowserPublish'
interface. They affect how URL traversal is accomplished. The
'browser_traverse' method is to lookup a name during URL
traversal. The implementation here simply uses 'guarded_getattr' to
retrieve an attribute, subject to security checks.
The 'browser_default' method is used to control what happens when a
URL points to a component, rather than to one of its methods. The
return value is a two-element tuple consisting if a new object and a
sequence of additional names to traverse the object with. In this
case, the component is returned with a tuple containing the name
'"form"'. When a URL like:
...../aContact/(p)edit
is used, the 'form' is displayed as if the URL had been:
...../aContact/(p)edit/form
At the end of the module, we create a singleton presentation
component and then create an object from it that, when called, will
return the component bound to it's context using acquisition.
In addition to the contact information methods, this presentation
component uses the 'update' method of contacts. The update method
isn't in the 'ContactInfo' interface we created in the last step.
We create a new interface, 'ContactEdit', in 'ContactEdit.py', that
extends the 'ContactInfo' interface with the 'update' method.
We need to register our new presentation component. The '__init__'
module in Step3 was getting rather complicated, so in this step, we
create a separate configuration module, in 'configure.py'. The
'__init__' module simply imports the configuration module and
imports the class and interfaces so that other file-system-based
modules can use them.
The configuration file includes the old logic from init and adds the
registration of the new editing component.
The configuration module also adds a security assertion for the
contact 'update' method. Note that we can't use the 'ContactEdit'
interface to make this assertion because it extends the
'ContactInfo' interface for which there is a conflicting assertion.
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/__init__.py ===
# do configuration
import configure; del configure
# export class and interfaces:
from Contact import Contact
from ContactInfo import ContactInfo
from ContactEdit import ContactEdit
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/configure.py ===
import App
import Interface
import Products.PageTemplate
import AccessControl, AccessControl.Permissions
import ZPublisher.Browser
from ContactInfo import ContactInfo
from ContactEdit import ContactEdit
from ContactEditPresentation import ContactEditPresentation
from Contact import Contact
from permissions import ManageContacts
# Protect contact view methods via the info interface:
AccessControl.protectClass(
Contact, AccessControl.Permissions.view,
inteface=ContactInfo,
instance=1,
)
# Register the contact factory
App.provideClass(Contact.Contact, permission=ManageContacts)
# Create a simple view presentation
ViewPresentation = Products.PageTemplate.SimplePresentation(
'viewContact', globals(),
permission = AccessControl.Permissions.view,
)
# Register this presentation under the name "view"
ComponentArchitecture.providePresentation(
ContactInfo, 'view', ZPublisher.Browser.BrowserPublish,
ViewPresentation)
# Register this presentation as the default
ComponentArchitecture.providePresentation(
ContactInfo, '', ZPublisher.Browser.BrowserPublish,
ViewPresentation)
# Allow the update method to be accessed by people with ManageContacts
AccessControl.protectClass(Contact, ManageContacts, method='update')
# Register the edit presentation
ComponentArchitecture.providePresentation(
ContactEdit, 'edit', ZPublisher.Browser.BrowserPublish,
ContactEditPresentation)
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/edit.pt ===
Edit contact
Enter the information about the contact.
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/permissions.py ===
ManageContacts = 'Manage Contacts'
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/view.pt ===
Contact Information
Contact information
Name:
FirstLast
Address:
Mailing address here
Email
foo@bar.com
From jim at zope.com Wed Oct 24 09:41:58 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - Contact.py:1.1 ContactCityState.py:1.1 ContactEdit.py:1.1 ContactEditPresentation.py:1.1 ContactInfo.py:1.1 Postal.py:1.1 README.txt:1.1 __init__.py:1.1 configure.py:1.1 edit.pt:1.1 permissions.py:1.1 stubpostal.py:1.1 view.pt:1.1
Message-ID: <200110241341.f9ODfw914527@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5
In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1/Step5
Added Files:
Contact.py ContactCityState.py ContactEdit.py
ContactEditPresentation.py ContactInfo.py Postal.py README.txt
__init__.py configure.py edit.pt permissions.py stubpostal.py
view.pt
Log Message:
work in progress. still pretty rough
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/Contact.py ===
import Persistence,
class Contact (Persistence.Persistent):
"""Contacts keep track of personal data, such as name, email
and postal address. All methods are protected."""
def __init__(self, first='', last='', email='', address='', pc=''):
self.update(first, last, email, address, pc)
def name(self):
return "%s %s" % (self._first, self._last)
def first(self):
return self._first
def last(self):
return self._last
def email(self):
return self._email
def address(self):
return self._address
def postal_code(self):
return self._pc
def update(self, first=None, last=None, email=None, address=None, pc=None):
if first is not None:
self._first = first
if last is not None:
self._last = last
if email is not None:
self._email = email
if address is not None:
self._address = address
if pc is not None:
self._pc = pc
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactCityState.py ===
import Postal
import ComponentArchitecture
class ContactCityState:
"Provide access to city and state information for a contact"
__implements__=Postal.PostalInfo
def __init__(self, contact):
self._contact=contact
lookup=ComponentArchitecture.getUtility(contact, Postal.PostalLookup)
info = lookup.lookup(contact.postal_code())
if info is None:
self._city, self._state = '', ''
else:
self._city, self._state = info.city(), info.state()
def city(self): return self._city
def state(self): return self._state
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactEdit.py ===
from ContactInfo import ContactInfo
from Contact import Contact
class ContactEdit(ContactInfo):
"Provides the ability to change basic contact information."
def update(first, last, email, address, pc):
"""Modifies contact data
'None' values are ignored.
"""
ContactEdit.assertImplementedBy(Contact)
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactEditPresentation.py ===
from ZPublisher.Browser import BrowserPublish
from AccessControl import ClassSecurityInfo
from permissions import ManageContacts
from Products.PageTemplate import PresentationPageTemplateFile
from App.common import absolute_url
from AccessControl.ZopeGuards import guarded_getattr
import Acquisition
class ContactEditPresentationClass(Acquisition.Explicit):
"""Provide an interface for editing a contact
"""
__implements__ = BrowserPublish
# Create a SecurityInfo for this class
security = ClassSecurityInfo()
security.setDefaultAccess('deny')
# All presentation components should provide this.
def getContext(self):
return self.aq_parent
# Input form
security.declareProtected(ManageContacts, 'form')
form = PresentationPageTemplateFile('edit.pt', globals())
# action method
security.declareProtected(ManageContacts, 'action')
def action(self, first, last, email, address, pc):
"Edit a contact"
self.getContext().update(first, last, email, address, pc)
return self.form()
def browser_traverse(self, request, name):
return guarded_getattr(self, name)
def browser_default(self, request):
return self, ('form',)
Globals.InitializeClass(ContactEditPresentationClass)
# Create the singleton component
ContactEditPresentation=ContactEditPresentationClass()
# and a binder for it
ContactEditPresentation=Acquisition.binder(ContactEditPresentation)
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactInfo.py ===
import Interface, Contact
class ContactInfo (Interface.Base):
"Provides access to basic contact information."
def first(): "Get the firt name"
def last(): "Get the last name"
def email(): "Get the electronic mail address"
def address(): "Get the postal address"
def postal_code(): "Get the postal code"
def name():
"""Gets the contact name.
The contact name is the first and last name together.
"""
ContactInfo.assertImplementedBy(Contact.Contact)
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/Postal.py ===
import Interface
class PostalInfo(Interface.Base):
"Provide information for postal codes"
def city():
"""Return the city associated with the postal code.
An empty string is returned if the city is unknown.
"""
def state():
"""Return the state associated with the postal code.
An empty string is returned if the state is unknown.
"""
class PostalLookup(Interface.Base):
"Provide postal code lookup"
def lookup(postal_code):
"""Lookup information for a postal code.
A PostalInfo is returned if the postal code is known. None is
returned otherwise.
"""
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/README.txt ===
Step5, adding features and utilities
In earlier steps we showed how to use simple classes to create
content objects that can be used in Zope. We also showed how to create
separate presentation components that provide web interfaces for
your objects. In this step, we'll learn about components for
providing additional functionality to your objects and
applications.
Components that provide new presentation independent logic are
called "application functionality" components. There are three
standard kinds of application functionality components. Features
provide new functionality for other components, typically content
components. Utilities and services provide stand-alone
functionality.
Utilities and services differ primarily in their role
with respect to the system overall and in their management. Services
are foundational. Many components and applications will depend on
services and services are moe prominent within the
compoent-management facilities. A common use of services is to
manage component. There are services for managing presentation,
features, utilities and services. Services may provide other
foundational capabilities, such as cataloging, version management,
and event channels. Utilities provide functionality that may be
used by a few applications or components.
In this step, we'll add a simple (trivial) feature for getting city
and state information for contacts. The functionality is provided
using a feature because the computation depends on a content
component, specifically a contact. The feature will look up the city
and state for a contact using the contact postal code. Looking up a
city and state for a postal code is not unique to working with
contacts however. We'll use a separate utility for doing the
postal-code lookup.
The 'Postal' module, in 'Postal.py', defines two interfaces needed
for postal code data lookup. The first interface defines how to get
postal-code data from postal code information objects. The second
interface defines postal code lookup.
The 'stubpostal' module, in 'stubpostal.py' provides an
implementation of the postal interfaces suitable for testing. This
module allows us to build and test our contact feature, but we don't
expect it to be used in a production environment.
Our configuration module registers the the stub utility using
'offerUtility'. The 'offerUtility' method takes two arguments, an
interface or a name [1], and a component. The interface identifies the
utility and the compnent provides the implementation. In this case,
we register an actual component, which is an instance of our lookup
class. This is in contrast to the way that presentation components
are registered. For presentation components, we don't register the
component directly, but register a callable object that returns the
presentation component for a given cntext.
We "offer" a component when we expect there to be multiple
implementations of the same component. We could have used
'provideUtility' instead, if our implementation was production
quality. It is an error to provide duplicate utilities with the same
interface. It is not an error to offer multiple utilities. An "offer"
is always superceded by a "provide".
The configuration module makes security assertions for the utility
classes, declaring the utilities and their methods to be public.
The 'ContactCityState' module, in 'ContactCityState.py' defines a
feature component for getting the city and state for a contact
object. The constructor for the component uses the contact
'postal_code' method, defined in the 'ContactInfo' interface to get
the contact postal code. It then retrieves a 'PostalLookup' utility
to get a 'PostalInfo' object for the given postal code. It looks up
the utility with 'ComponentArchitecture.getUtility', which takes two
arguments [2], an object and a desired interface. The first argument is
passed to allow the utility lookup to be context dependent.
Utilities can be registered in specific locations (e.g. folders) in
a Zope object system. By passing an object to getUtility, we cause
local utility registries, if any, to be searched. The constructor
gets the city and state, which it saves for later use in the 'city'
and 'state' methods.
The feature is registered with
'ComponentArchitecture.provideFeature' in the configuration
module. The 'provideFeature' function takes three arguments: an
input interface, an output interface, and a callable object that
creates a feature for a given context. The feature can be used with
any component that provides the input interface. In this example, we
pass the class as the callable object. Features are like
presentation components in that they are context-dependent and must
be registered with a callable object that creates a component for a
given context. In this example, we chose to create a separate
component for each context. We could, instead, have used acquisition
to manage context as we did for the edit preentation in step 4.
To use the feature, we need to look it up with the
'ComponentArchitecture.getFeature' method. For example, consider a
Python script that wants to find out how many contacts, in a list of
contacts live in Virginia::
nva=0
for contact in aListOfContacts:
info = ComponentArchitecture.getFeature(contact, PostalInfo, None)
if info is not None and info.state() == 'Virginia':
nva = nva+1
In this example, 'getFeature' is passed three arguments, an object,
an interface, and a default value. In this case, the object passed
specifies both a location to search for the feature and an object
that the feature will be passed when it is created (or bound). It's
worth noting that if the object passed as the first argument already
implements the interface passed as the second argument, the object
will be returned.
It's worth noting that the feature can be used with any
implementation of 'ContactInfo' and 'PostalLookup'. If there were
multiple or alternative contact implementations, this feature would
be applicable. Similarly, the feature works equally well with a
porduction-quality postal-information lookup component and with the
stub that we've provided here.
------------------------------------------------------------------------------
[1] -- We allow either an interface or a name. The name is only used
in cases where defining an interface is too much
bother. Interfaces are prefered.
[2] -- An optional third argument can provide a default. If no default
is specified, an error is raised if the lookup fails.
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/__init__.py ===
# do configuration
import configure; del configure
# export class and interfaces:
from Contact import Contact
from ContactInfo import ContactInfo
from ContactEdit import ContactEdit
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/configure.py ===
import App
import Interface
import Products.PageTemplate
import AccessControl, AccessControl.Permissions
import ZPublisher.Browser
from ContactInfo import ContactInfo
from Contact import Contact
from permissions import ManageContacts
# Protect contact view methods via the info interface:
protectInterface(ViewPermission, Contact, ContactInfo)
protectInstance(ViewPermission, Contact)
# Register the contact factory
App.provideClass(Contact.Contact, permission=ManageContacts)
# Create a simple view presentation
ViewPresentation = SimplePresentation(
'viewContact', globals(),
permission = view,
)
# Register this presentation under the name "view"
ComponentArchitecture.providePresentation(
ContactInfo, 'view', BrowserPublish,
ViewPresentation)
# Register this presentation as the default
ComponentArchitecture.providePresentation(
ContactInfo, '', BrowserPublish,
ViewPresentation)
##############################################################################
# Edit presentation
from ContactEdit import ContactEdit
from ContactEditPresentation import ContactEditPresentation
# Allow the update method to be accessed by people with ManageContacts
protectMethods(ManageContacts, Contact, 'update')
# Register the edit presentation
ComponentArchitecture.providePresentation(
ContactEit, '', ZPublisher.Browser.BrowserPublish,
ContactEditPresentation)
##############################################################################
# Postal code utility
import Postal, stubpostal
# Offer out postal info utility. Hopefully, someone will provide a
# replacement.
ComponentArchitecture.offerUtility(Postal.PostalLookup, stubpostal.lookup())
# Allow our postal utilities to be used by anyone:
AccessControl.unprotectClass(
stubpostal.lookup, interface=Postal.PostalLookup, instance=1)
AccessControl.unprotectClass(
stubpostal.info, interface=Postal.PostalInfo, instance=1)
##############################################################################
# Contact city and state lookup
from ContactCityState import ContactCityState
# Provide the city state feature
ComponentArchitecture.provideFeature(ContactInfo, PostalInfo, ContactCityState)
# Provide public access to the feature:
AccessControl.unprotectClass(
ContactCityState, interface=Postal.PostalInfo, instance=1)
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/edit.pt ===
Edit contact
Enter the information about the contact.
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/permissions.py ===
ManageContacts = 'Manage Contacts'
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/stubpostal.py ===
# Stub postal utility implemantation
import Postal
class info:
__implements__ = Postal.PostalInfo
def __init__(self, city, state):
self._city, self._state = city, state
def city(self): return self._city
def state(self): return self._state
class lookup:
__implements__ = Postal.PostalLookup
_data = {
'22401': ('Fredericksburg', 'Virginia'),
'44870': ('Sandusky', 'Ohio'),
'90051': ('Los Angeles', 'California'),
}
def lookup(self, postal_code):
data=self._data.get(postal_code)
if data is not None: data = info(*data)
return data
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/view.pt ===
Contact Information
Contact information
Name:
FirstLast
Address:
Mailing address here
Email
foo@bar.com
From jim at zope.com Fri Oct 26 10:26:12 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 - slides.sdd.gz:1.1
Message-ID: <200110261426.f9QEQC313065@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1
In directory cvs.zope.org:/tmp/cvs-serv13058
Added Files:
slides.sdd.gz
Log Message:
StarOffice slides for Chapter 1
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/slides.sdd.gz === (632/732 lines abridged)
*; ] xEL\$$! a}-r(&pʑPHTv]}XP/Utq-cUO}" {3 N~ԿLүJpKTRS!.]p.V w wwlC{q ^ H $@K$)@*- h d@
|5hv@{rt
Btz =^@o_τ~ A``0\F #kQh`0(qx L &}0Lt`0(J2`0@P p3pp+
X,ˀJ
^EA4?Z`ŘS4:5QE{/
|CiSGc
k|#lK˃Ӄs*
/
N_0b^%w.kWiEPnM|)*[4b0el+3/s_PB\P0'h-'VTXP./$t^ゖ[1bt¨%D-lvSVJmOln%3Sx`:v%좀'[S+gʍ3}nsV*"6&ߪo!`n"bA"a"O 8E3&"e:<$x}9[m;@v,H4c^^q
([/Z#17KJo,7b.TA
U U'OՓW)kzF=STjmMKzTHЮ.O@:CP:^Et$^CGtK"Za