From chrism at zope.com Tue Feb 12 22:06:11 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 - Contact.zcml:1.2 README.txt:1.4 Message-ID: <200202130306.g1D36Bt17514@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 In directory cvs.zope.org:/tmp/cvs-serv17445 Modified Files: Contact.zcml README.txt Log Message: Updating Python Programmer tutorial Step 1. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/Contact.zcml 1.1 => 1.2 === + - + - + + + === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/README.txt 1.3 => 1.4 === In this step, we present the minimum tasks necessary to create a class - usable in Zope. + usable in Zope. The first task is to create a 'Contact' package in the Zope - 'Products' package. Components are contained in Zope file-based - products [1], which are simply Python packages that are sub-packages - of the Zope 'Products' package. A package is simply a directory that - has a Python '__init__' module. We'll create the directory and add - the file '__init__.py'. An empty file will do four our purposes. + package. Components are contained in Zope file-based products [1], + which are simply Python packages that are intialized within the + 'products.zcml' XML configuration file. A package is simply a + directory that has a Python '__init__' module. We'll create the + directory and add the file '__init__.py'. An empty file will do four + our purposes. We also create the file 'Contact.py' containing the class definition. This class doesn't use *any* Zope-specific classes. The @@ -27,36 +28,39 @@ with a Zope configuration file, 'Contact.zcml'. A Zope configuration file is an XML [3] file that contains elements that express configuration directives. The configuration file in this - example includes two directives. The first directive defines a Zope - permission. The second directive, 'zmi:provideClass', registers our - class. The class being registered is specified with the 'name' - attribute. The 'permission' attribute 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 attributes can be - used to provide additional meta data, however, the meta-data is - infered from class meta-data. + example includes two directives. The first directive, + security:protectClass protects some methods of the Contact class + with a predetermined Zope permission. The second directive, + 'zmi:provideClass', registers our class. The class being registered + is specified with the 'name' attribute. The 'permission_id' + attribute 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. The "title" attribute is used to specify the ZMI title for the + class in the Add list. Additional attributes can be used to provide + additional meta data, however, the meta-data is infered from class + meta-data. In the configiration file, the class was provided using the string - ".Contact". This bears some explanation. First, the string - ".Contact" is an abreviation of the string: - "Zope.Products.Contact.Contact.Contact", which is the full name of the + "Zope.Contact.". This bears some explanation. First, the string + "Zope.Contact." is an abreviation of the string: + "Zope.Contact.Contact.Contact", which is the full name of the class object. The full name is a combination of the class module - name, "Zope.Products.Contact.Contact" and the class name, "Contact". A + name, "Zope.Contact.Contact" and the class name, "Contact". A full name can be abreviated using the following rules: - If the full name begins with "Zope.Products.", then the "Zope.Products" prefix can be omitted. A leading dot implies the 'Zope.Products' - package. + package. So ".Contact" implies "Zope.Products.Contact" - If the end of the name has repeating parts, as in ".Contact.Contact.Contact", then the repeated parts other than the first can be omitted. - We could have used the same mechanism to specify the permission. The - value of the permission attribute should be either a name used in a - 'definePermission' directive, or the name of a permission defined in - a module. + We could have used the same mechanism to specify the + permission_id. The value of the permission_id attribute should be + either a name used in a 'definePermission' directive (not discussed + here), or the name of a permission defined in a module. The 'zmi:provideClass' directive accomplishes two things: @@ -69,7 +73,7 @@ An identifier for the factory is generated from the registered class module and class name. In this case, the module is - 'Zope.Products.Contact.Contact' and the class is + 'Zope.Contact.Contact' and the class is 'Contact'. Abbreviations are allowed, as in configuration files. A different identifier could have been provided in the 'zmi:provideClass' directive. @@ -81,14 +85,19 @@ Zope's management interface for adding objects to containers. Simply creating the configuration file isn't enough. The - configuration file must be copied (or linked) to a Zope 'Products' - package directory, typically the one containing the 'Contact' - package. Copying the configuration file makes installing components - an explicit step by the site manager. The site manager can disable a - product by simply removing it's configuration file from the - 'Products' package. + configuration file must be mentioned (by convention) within the Zope + top-level 'products.zcml' file. Manipulating this configuration + file makes installing components an explicit step by the site + manager. The site manager can disable a product by simply removing + it's the mention of its configuration file from the 'products.zcml' + file. - With this registration, contacts can be added to Zope folders. + Here's the entry we put in to the 'products.zcml' file in order to + register the Contact class:: + + + + With this registration, contacts can now 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 From chrism at zope.com Tue Feb 12 22:32:36 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 - Contact.zcml:1.3 README.txt:1.5 Message-ID: <200202130332.g1D3WaQ24793@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 In directory cvs.zope.org:/tmp/cvs-serv24776/Step1 Modified Files: Contact.zcml README.txt Log Message: Extended to step2. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/Contact.zcml 1.2 => 1.3 === > - - - + 1.5 === usable in Zope. - The first task is to create a 'Contact' package in the Zope - package. Components are contained in Zope file-based products [1], - which are simply Python packages that are intialized within the - 'products.zcml' XML configuration file. A package is simply a - directory that has a Python '__init__' module. We'll create the - directory and add the file '__init__.py'. An empty file will do four - our purposes. + The first task is to create a 'Contact' package in the Zope package + (it's in Zope3/lib/python/Zope). Components are contained in Zope + file-based products [1], which are simply Python packages that are + intialized within the 'products.zcml' XML configuration file at the + root of the Zope3 software home. A package is simply a directory + that has a Python '__init__' module. We'll create the directory and + add the file '__init__.py'. An empty file will do four our purposes. We also create the file 'Contact.py' containing the class definition. This class doesn't use *any* Zope-specific classes. The From chrism at zope.com Tue Feb 12 22:32:36 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 - Contact.zcml:1.2 Message-ID: <200202130332.g1D3WaK24794@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 In directory cvs.zope.org:/tmp/cvs-serv24776/Step2 Modified Files: Contact.zcml Log Message: Extended to step2. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/Contact.zcml 1.1 => 1.2 === + - + - + - + From chrism at zope.com Tue Feb 12 22:47:02 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 - products.zcml:1.1 Contact.zcml:1.4 README.txt:1.6 Message-ID: <200202130347.g1D3l2D28464@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 In directory cvs.zope.org:/tmp/cvs-serv27706 Modified Files: Contact.zcml README.txt Added Files: products.zcml Log Message: Whoops, corrected the problem with definePermission and added a bit about products.zcml. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/products.zcml === === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/Contact.zcml 1.3 => 1.4 === > - + + + === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/README.txt 1.5 => 1.6 === express configuration directives. The configuration file in this example includes two directives. The first directive, - security:protectClass protects some methods of the Contact class - with a predetermined Zope permission. The second directive, - 'zmi:provideClass', registers our class. The class being registered - is specified with the 'name' attribute. The 'permission_id' - attribute 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. The "title" attribute is used to specify the ZMI title for the - class in the Add list. Additional attributes can be used to provide + security:permission defines a permission that we'll use for the + Contact product. The second directive, 'zmi:provideClass', + registers our class. The class being registered is specified with + the 'name' attribute. The 'permission_id' attribute 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. The + "title" attribute is used to specify the ZMI title for the class in + the Add list. Additional attributes can be used to provide additional meta data, however, the meta-data is infered from class meta-data. @@ -49,9 +49,10 @@ name, "Zope.Contact.Contact" and the class name, "Contact". A full name can be abreviated using the following rules: - - If the full name begins with "Zope.Products.", then the "Zope.Products" - prefix can be omitted. A leading dot implies the 'Zope.Products' - package. So ".Contact" implies "Zope.Products.Contact" + - If the full name begins with "Zope.Products.", then the + "Zope.Products" prefix can be omitted. A leading dot implies the + 'Zope.Products' package. So, for example, ".Contact" implies + "Zope.Products.Contact" - If the end of the name has repeating parts, as in ".Contact.Contact.Contact", then the repeated parts other than the @@ -59,8 +60,8 @@ We could have used the same mechanism to specify the permission_id. The value of the permission_id attribute should be - either a name used in a 'definePermission' directive (not discussed - here), or the name of a permission defined in a module. + either a name used in a 'security:permission' directive or the name + of a permission defined in a module. The 'zmi:provideClass' directive accomplishes two things: From chrism at zope.com Tue Feb 12 22:50:27 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 - README.txt:1.7 Message-ID: <200202130350.g1D3oRM28927@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 In directory cvs.zope.org:/tmp/cvs-serv28918 Modified Files: README.txt Log Message: Added a bit about using the products.zcml in the step1 directory. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/README.txt 1.6 => 1.7 === + The products.zcml file provided in the Step1 directory should be + copied to the Zope3 software home, or you can edit the existing + products.zcml file. + With this registration, contacts can now be added to Zope folders. Simply adding contacts to Zope folders is of limited usefulness. We From chrism at zope.com Tue Feb 12 22:52:30 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 - Contact.zcml:1.5 Message-ID: <200202130352.g1D3qUS29931@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 In directory cvs.zope.org:/tmp/cvs-serv29924 Modified Files: Contact.zcml Log Message: Removed protectClass declaration. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/Contact.zcml 1.4 => 1.5 === /> - - From chrism at zope.com Tue Feb 12 22:53:55 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 - Contact.zcml:1.3 Message-ID: <200202130353.g1D3rtY30069@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 In directory cvs.zope.org:/tmp/cvs-serv30031 Modified Files: Contact.zcml Log Message: Re-added permission declaration after finding out it actually works. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/Contact.zcml 1.2 => 1.3 === > - + From chrism at zope.com Wed Feb 13 02:57:53 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 - products.zcml:1.1 Message-ID: <200202130757.g1D7vrm28401@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 In directory cvs.zope.org:/tmp/cvs-serv28382/Step2 Added Files: products.zcml Log Message: Added products.zcml to Step2 and Step3, modified necessary files in Step3 to have a working component. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/products.zcml === From chrism at zope.com Wed Feb 13 02:57:53 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 - products.zcml:1.1 Contact.zcml:1.2 ContactViewPresentation.py:1.2 README.txt:1.4 Message-ID: <200202130757.g1D7vrN28402@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 In directory cvs.zope.org:/tmp/cvs-serv28382/Step3 Modified Files: Contact.zcml ContactViewPresentation.py README.txt Added Files: products.zcml Log Message: Added products.zcml to Step2 and Step3, modified necessary files in Step3 to have a working component. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/products.zcml === === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/Contact.zcml 1.1 => 1.2 === + + + + + + + + + - - - - - - - - - + + === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/ContactViewPresentation.py 1.1 => 1.2 === +from Zope.PageTemplate import SimpleViewClass from IContactInfo import IContactInfo # Create a simple view presentation -ContactViewPresentation = SimplePresentationClass( - 'view.pt', applicable_for=IContactInfo) +ContactViewPresentation = SimpleViewClass( + 'view.pt', used_for=IContactInfo) === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/README.txt 1.3 => 1.4 === very simple, consisting of a single page, expressed as a page template. Page templates provide a convenience functon, - 'SimplePresentationClass' for creating a web presentation class from + 'SimpleViewClass' for creating a web presentation class from a page template source file. We use this function to create our view presentation class in the 'ContactViewPresentation' module in the file 'ContactViewPresentation.py'. We pass the function the name of @@ -92,7 +92,7 @@ assertion for the new presentation class. In this case, we protect a single method, 'index', wich is used to render the presentation. - The configuration file contains a 'browser:defaultPresentation' + The configuration file contains a 'browser:defaultView' directive that registers the view presentation. The 'for' attribute specifies the interface that the presentation applies to. By specifying the name of the 'IContactInfo' interface, we specify that @@ -104,7 +104,7 @@ ...../aContact/view/ - The 'component' attribute specifies the name of a callable + The 'factory' attribute specifies the name of a callable object that returns a presentation component when called with an object that implements the given interface. In this case, we specify the presentation component class. The presentation was registered as From chrism at zope.com Wed Feb 13 14:40:50 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 - products.zcml:1.1 Contact.zcml:1.3 ContactEditPresentation.py:1.4 ContactViewPresentation.py:1.2 IContact.py:1.2 README.txt:1.6 Message-ID: <200202131940.g1DJeoo15605@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 In directory cvs.zope.org:/tmp/cvs-serv15582 Modified Files: Contact.zcml ContactEditPresentation.py ContactViewPresentation.py IContact.py README.txt Added Files: products.zcml Log Message: Turned lies into truth. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/products.zcml === === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/Contact.zcml 1.2 => 1.3 === - - - - - - - + + + + + + + + - - - + - + - + - + - + + === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactEditPresentation.py 1.3 => 1.4 === -from Zope.Products.PageTemplate import PresentationPageTemplateFile +from Zope.PageTemplate import PageTemplateFile from IContactEdit import IContactEdit -class ContactEditPresentationClass(AttributePublisher): +class ContactEditPresentation(AttributePublisher.AttributePublisher): """Provide an interface for editing a contact """ @@ -17,7 +17,7 @@ __used_for__=IContactEdit # Input form - index = PresentationPageTemplateFile('edit.pt', globals()) + index = PageTemplateFile('edit.pt', globals()) # action method def action(self, first, last, email, address, pc): === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactViewPresentation.py 1.1 => 1.2 === +from Zope.PageTemplate import SimpleViewClass from IContactInfo import IContactInfo # Create a simple view presentation -ContactViewPresentation = SimplePresentationClass( - 'view.pt', applicable_for=IContactInfo) +ContactViewPresentation = SimpleViewClass( + 'view.pt', used_for=IContactInfo) === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/IContact.py 1.1 => 1.2 === from Contact import Contact -class IContactInfo(Interface): +class IContact(Interface): "Marker for objects that provide specific behavior" implements(Contact, IContact) === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/README.txt 1.5 => 1.6 === prevents someone from mistakedly configuring the component for use with other (incompatable) interfaces. The '__used_for__' - attribute is optional. Only one interfac can be specifeid. One + attribute is optional. Only one interface can be specified. One could concievably create a component that is applicable to several alternative interfaces, in which case, the '__applicable__' attribute would not be set. We modify the configuration file to add a security assertion for the - new component and we add a 'browser::presentation' directive to + new component and we add a 'browser::view' directive to register the component. We also need to add a security assertion for the 'update' method of From chrism at zope.com Wed Feb 13 15:54:12 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - products.zcml:1.1 Contact.zcml:1.4 ContactEditPresentation.py:1.5 ContactViewPresentation.py:1.3 IContact.py:1.2 README.txt:1.5 Message-ID: <200202132054.g1DKsCO02834@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv2822 Modified Files: Contact.zcml ContactEditPresentation.py ContactViewPresentation.py IContact.py README.txt Added Files: products.zcml Log Message: Turned lies into truth. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/products.zcml === === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/Contact.zcml 1.3 => 1.4 === > - + - - - - + + + + - - - - - - - - - + + + + + + + - + - + - + - + - + - + + === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactEditPresentation.py 1.4 => 1.5 === +from Zope.Publisher.Browser import AttributePublisher from Zope.PageTemplate import PageTemplateFile from IContactEdit import IContactEdit -class ContactEditPresentation(AttributePublisher): +class ContactEditPresentation(AttributePublisher.AttributePublisher): """Provide an interface for editing a contact """ @@ -23,4 +23,4 @@ def action(self, first, last, email, address, pc): "Edit a contact" self.getContext().update(first, last, email, address, pc) - return self.index() + return self.view() === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactViewPresentation.py 1.2 => 1.3 === +from Zope.PageTemplate import SimpleViewClass from IContactInfo import IContactInfo # Create a simple view presentation -ContactViewPresentation = SimplePresentationClass( - 'view.pt', globals(), used_for=IContactInfo) +ContactViewPresentation = SimpleViewClass( + 'view.pt', used_for=IContactInfo) === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/IContact.py 1.1 => 1.2 === from Contact import Contact -class IContactInfo(Interface): +class IContact(Interface): "Marker for objects that provide specific behavior" implements(Contact, IContact) === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/README.txt 1.4 => 1.5 === Components that provide new presentation-independent logic are called "application-functionality" components. There are three - standard kinds of application-functionality components. Features + standard kinds of application-functionality components. Adapters 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 + 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 more prominent within the compoent-management facilities. A common use of services is to - manage components. There are services for managing presentation components, - 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. + manage components. There are services for managing presentation + components, 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 + In this step, we'll add a simple (trivial) adapter 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 + using an adapter because the computation depends on a content + component, specifically a contact. The adapter 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. @@ -44,17 +44,17 @@ module allows us to build and test our contact feature, but we don't expect it to be used in a production environment. - The configuration file registers the the stub utility using - the 'utility' directive. The 'utility' directive uses two + The configuration file registers the the stub utility using the + 'utility' directive. The 'utility' directive uses two attributes. The 'provides' attribute specifies the interface that - the component provides. The 'component' attribute specifies the - 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 context. + the component provides. The 'factory' attribute specifies the + component. The interface identifies the utility and the component + 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 context. The configuration module makes security assertions for the utility classes, declaring the utilities and their methods to be public. @@ -74,30 +74,30 @@ 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 'feature' directive in the configuration - file. The 'feature' directive uses three attributes. The 'for' - attribute specifies the interface of the objects the feature is used - for. The 'component' and 'provides' attributes specify the - component and the provided interface. 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. + The adapter is registered with the 'adapter' directive in the + configuration file. The 'adapter' directive uses three + attributes. The 'for' attribute specifies the interface of the + objects the feature is used for. The 'factory' and 'provides' + attributes specify the component and the provided interface. The + adapter can be used with any component that provides the input + interface. In this example, we pass the class as the callable + object. Adapters 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. To use the feature, we need to look it up with the - 'Zope.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:: + 'Zope.ComponentArchitecture.getAdapter' method. For example, + consider a Python script that wants to find out how many contacts, + in a list of contacts live in Virginia:: - from Zope.ComponentArchitecture import getFeature + from Zope.ComponentArchitecture import getAdapter nva=0 for contact in aListOfContacts: - info =getFeature(contact, IPostalInfo, None) + info =getAdapter(contact, IPostalInfo, None) if info is not None and info.state() == 'Virginia': nva = nva+1 - In this example, 'getFeature' is passed three arguments, an object, + In this example, 'getAdapter' 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 @@ -105,10 +105,10 @@ 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 + It's worth noting that the adapter 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 + multiple or alternative contact implementations, this adapter would + be applicable. Similarly, the adapter works equally well with a production-quality postal-information lookup component and with the stub that we've provided here. From chrism at zope.com Thu Feb 14 17:59:57 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - README.txt:1.6 Message-ID: <200202142259.g1EMxvX11974@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv11942/Chapter1/Step5 Modified Files: README.txt Log Message: Committing Sean Bowman's corrections. Thanks Sean! === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/README.txt 1.5 => 1.6 === foundational. Many components and applications will depend on services and services are more prominent within the - compoent-management facilities. A common use of services is to + component-management facilities. A common use of services is to manage components. There are services for managing presentation components, features, utilities and services. Services may provide other foundational capabilities, such as cataloging, version @@ -39,7 +39,7 @@ postal-code data from postal code information objects. The second interface defines postal code lookup. - The 'stubpostal' module, in 'stubpostal.py' provides an + 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. @@ -59,10 +59,10 @@ 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 + 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 + 'postal_code' method, defined in the 'IContactInfo' 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 'Zope.ComponentArchitecture.getUtility', which @@ -91,13 +91,13 @@ in a list of contacts live in Virginia:: from Zope.ComponentArchitecture import getAdapter - nva=0 + nva = 0 for contact in aListOfContacts: - info =getAdapter(contact, IPostalInfo, None) + info = getAdapter(contact, IPostalInfo, None) if info is not None and info.state() == 'Virginia': - nva = nva+1 + nva = nva+1 - In this example, 'getAdapter' is passed three arguments, an object, + In this example, 'getAdapter' 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 @@ -105,8 +105,8 @@ implements the interface passed as the second argument, the object will be returned. - It's worth noting that the adapter can be used with any - implementation of 'ContactInfo' and 'PostalLookup'. If there were + This adapter can be used with any + implementation of 'IContactInfo' and 'IPostalLookup'. If there were multiple or alternative contact implementations, this adapter would be applicable. Similarly, the adapter works equally well with a production-quality postal-information lookup component and with the @@ -116,7 +116,7 @@ [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. + bother. Interfaces are preferred. [2] -- An optional third argument can provide a default. If no default is specified, an error is raised if the lookup fails. From chrism at zope.com Thu Feb 14 18:00:29 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 - README.txt:1.4 Message-ID: <200202142300.g1EN0Sb12072@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 In directory cvs.zope.org:/tmp/cvs-serv11942/Chapter1/Step2 Modified Files: README.txt Log Message: Committing Sean Bowman's corrections. Thanks Sean! === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/README.txt 1.3 => 1.4 === 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 + 'Contact.py', remains unchanged. We add a security assertion in the configuration file. The security assertion is made with the 'security:protectClass' directive. The class protected is specified with the 'name' attribute and the permission used is specified in @@ -13,7 +13,7 @@ specified permission also applies to instances of the class. In this example we protected the methods for getting contact - information with the Zope 'View' permission. With this change, we + 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 @@ -29,7 +29,7 @@ ..../aContact/view.pt - When this happens the template variable, 'here' is bound to the + 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. @@ -39,7 +39,7 @@ '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. + sometimes lead to unpleasant surprises. In Zope 3, objects will still take advantage of acquisition, but in much more explicit ways. Most objects will have a searchable @@ -67,7 +67,7 @@ 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 + 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 @@ -79,5 +79,5 @@ ---------------------------------------------------------------------- [1] -- This URL uses a URL parameter to indicate that an object with -the name "view.pt" should be looked up in the context or it's -containers. + the name "view.pt" should be looked up in the context or its + containers. From chrism at zope.com Thu Feb 14 18:00:29 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 - README.txt:1.7 Message-ID: <200202142300.g1EN0S512070@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 In directory cvs.zope.org:/tmp/cvs-serv11942/Chapter1/Step4 Modified Files: README.txt Log Message: Committing Sean Bowman's corrections. Thanks Sean! === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/README.txt 1.6 => 1.7 === - An assertion that instances of the class implement the - 'BrowserPublish' interface by defining the '__implements__' + 'IBrowserPublisher' interface by defining the '__implements__' attribute. This example illustrates how to assert interfaces in a class definition. - An implementation of the 'browser_traverse' method of the - 'IBrowserPublish' interface. In this case,'browser_traverse' + 'IBrowserPublisher' interface. In this case, the 'browser_traverse' method provides publishing of attribute values. This implementation handles names with an ".html" suffix by removing the suffix before looking up an attribute. - An implementation of the 'browser_default' method of the - 'IBrowserPublish' interface. The 'browser_default' method is used + 'IBrowserPublisher' interface. 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 can be either: @@ -47,12 +47,12 @@ sequence of additional names to traverse the object with. In this case, we use the simpler form and simply return the string - "index.html". Subclasses must provide an attribute, 'index' that provides - their default interface, or they must override this method to + "index.html." Subclasses must provide an 'index' attribute that provides + their default presentation, or they must override this method to supply a different name. Presentation components present other components. We refer to the - component presented by a presentation component as it's "context". + component presented by a presentation component as its "context." The methods of a presentation component need access to the presentation context. They do so by calling the 'getContext' method that must be provided by a presentation component. The class @@ -63,7 +63,7 @@ form. It implements this using a 'PresentationPageTemplateFile', with the source in the file 'edit.pt'. - The action of the form is "action.html", which names the 'action' method of + The action of the form is "action.html," which names the 'action' method of the component. The 'action' method applies the update and redisplays the editing form. @@ -74,17 +74,17 @@ extends the 'IContactInfo' interface with the 'update' method. The presentation component uses and thus is only applicable to - object objects that implement the 'IContactEdit' interface. We + objects that implement the 'IContactEdit' interface. We specify this by defining the '__used_for__' attribute. This - prevents someone from mistakedly configuring the component for use - with other (incompatable) interfaces. The '__used_for__' + prevents someone from mistakenly configuring the component for use + with other (incompatible) interfaces. The '__used_for__' attribute is optional. Only one interface can be specified. One - could concievably create a component that is applicable to several - alternative interfaces, in which case, the '__applicable__' + could conceivably create a component that is applicable to several + alternative interfaces, in which case the '__used_for__' attribute would not be set. We modify the configuration file to add a security assertion for the - new component and we add a 'browser::view' directive to + new component, and we add a 'browser:view' directive to register the component. We also need to add a security assertion for the 'update' method of @@ -92,7 +92,7 @@ permissions, we need to use a more complicated form for the security assertions. Rather than using a single empty element for the assertion, we use three empty elements grouped by a non-empty - 'security:protectClass' element. When need to include an explicit assertion + 'security:protectClass' element. We need to include an explicit assertion for protecting class instances (as opposed to their methods) whenever we use the complex form of 'security:protectClass'. @@ -110,7 +110,7 @@ The tabs are defined using a new 'IContact' interface, defined in the 'IContact' module. This is a "marker" interface. It doesn't - actually specify any contacts but provides a way of very + actually specify any methods but provides a way of very specifically tagging certain classes (usually one). We do this for tabs because we usually want very specific control over the tabs that a content object displays. From chrism at zope.com Thu Feb 14 18:00:29 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 - README.txt:1.5 Message-ID: <200202142300.g1EN0Si12071@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 In directory cvs.zope.org:/tmp/cvs-serv11942/Chapter1/Step3 Modified Files: README.txt Log Message: Committing Sean Bowman's corrections. Thanks Sean! === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/README.txt 1.4 => 1.5 === 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". + 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 @@ -15,8 +15,8 @@ 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. + "contract" or a "promise." A component "implements" ("realizes," + "satisfies," "provides") its interfaces. Interfaces group (or "classify") objects based on their behavior. Objects that provide the same interface are similar and @@ -34,7 +34,7 @@ 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". + 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 @@ -56,7 +56,7 @@ example, we asserted the instance 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 + example. There are two advantages to specifying the interface externally, as we have here: - The interface assertion doesn't require modifying the class @@ -66,7 +66,7 @@ - The 'implements' function performs minimal conformance checks and raises errors if the class can be determined not to implement the - interface. [4] + interface [4]. Now that contacts are components, we can associate presentation components with them. @@ -75,22 +75,22 @@ contacts. We normally create components using Python classes 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, - 'SimpleViewClass' for creating a web presentation class from + template. Page templates provide a convenience function, + 'SimpleViewClass', for creating a web presentation class from a page template source file. We use this function to create our view presentation class in the 'ContactViewPresentation' module in the file 'ContactViewPresentation.py'. We pass the function the name of the template file, relative to the package. We also provide an - interfact with the 'used_for' keyword argument. The + interface with the 'used_for' keyword argument. The optional 'used_for' argument is used to say that the component applicable to objects that implement the 'IContactInfo' interface. - By specifying the interface that presented object must implement, we - can protect the component from being configures to present - incompatable objects. + By specifying the interface that presented objects must implement, we + can protect the component from being configured to present + incompatible objects. The configuration file is modified. First, we add a security assertion for the new presentation class. In this case, we protect a - single method, 'index', wich is used to render the presentation. + single method, 'index', which is used to render the presentation. The configuration file contains a 'browser:defaultView' directive that registers the view presentation. The 'for' attribute @@ -122,14 +122,14 @@ ------------------------------------------------------------------------------ [1] -- Some would argue that abstract base classes could provide the - same capability. This is true, however, we think interfaces provide + 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 must satisfy 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. @@ -161,6 +161,6 @@ [4] -- The 'implements' function 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. + the interface. The best we can do is check for gross errors, like + missing methods or mismatched arguments. From chrism at zope.com Thu Feb 14 18:00:29 2002 From: chrism at zope.com (Chris McDonough) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 - README.txt:1.8 Message-ID: <200202142300.g1EN0Sc12073@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 In directory cvs.zope.org:/tmp/cvs-serv11942/Chapter1/Step1 Modified Files: README.txt Log Message: Committing Sean Bowman's corrections. Thanks Sean! === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/README.txt 1.7 => 1.8 === (it's in Zope3/lib/python/Zope). Components are contained in Zope file-based products [1], which are simply Python packages that are - intialized within the 'products.zcml' XML configuration file at the + initialized within the 'products.zcml' XML configuration file at the root of the Zope3 software home. A package is simply a directory that has a Python '__init__' module. We'll create the directory and - add the file '__init__.py'. An empty file will do four our purposes. + add the file '__init__.py'. An empty file will do for our purposes. We also create the file 'Contact.py' containing the class definition. This class doesn't use *any* Zope-specific classes. The - class provides methods for accessing contact data and for modifying - the data. + class provides methods for accessing and modifying the contact data. This class mixes in the 'Persistent' base class. Doing so allows instances of the class to be stored in Zope's object database and @@ -29,7 +28,7 @@ configuration file is an XML [3] file that contains elements that express configuration directives. The configuration file in this example includes two directives. The first directive, - security:permission defines a permission that we'll use for the + 'security:permission', defines a permission that we'll use for the Contact product. The second directive, 'zmi:provideClass', registers our class. The class being registered is specified with the 'name' attribute. The 'permission_id' attribute is used to @@ -38,16 +37,16 @@ through the web or through code managed through the web. The "title" attribute is used to specify the ZMI title for the class in the Add list. Additional attributes can be used to provide - additional meta data, however, the meta-data is infered from class + additional meta data; however, the meta-data is inferred from class meta-data. - In the configiration file, the class was provided using the string + In the configuration file, the class was provided using the string "Zope.Contact.". This bears some explanation. First, the string - "Zope.Contact." is an abreviation of the string: + "Zope.Contact." is an abbreviation of the string "Zope.Contact.Contact.Contact", which is the full name of the class object. The full name is a combination of the class module name, "Zope.Contact.Contact" and the class name, "Contact". A - full name can be abreviated using the following rules: + full name can be abbreviated using the following rules: - If the full name begins with "Zope.Products.", then the "Zope.Products" prefix can be omitted. A leading dot implies the @@ -90,7 +89,7 @@ top-level 'products.zcml' file. Manipulating this configuration file makes installing components an explicit step by the site manager. The site manager can disable a product by simply removing - it's the mention of its configuration file from the 'products.zcml' + the mention of its configuration file from the 'products.zcml' file. Here's the entry we put in to the 'products.zcml' file in order to @@ -107,7 +106,7 @@ 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 + security assertions that make them accessible. Conceivably, we could use contacts from trusted (file-based) code. In the next step, we'll make security assertions for the class, @@ -116,18 +115,18 @@ ------------------------------------------------------------------------------ [1] -- Components can also be developed through the web and contained - in the Zope object database. This tutorial, however, only discussed + in the Zope object database. This tutorial, however, only discusses file-based products. -[2] -- There are a few additional restrictions, sometimes refered to - as the "rules of persistence", that persistent class +[2] -- There are a few additional restrictions, sometimes referred 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 + 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. From jim at zope.com Thu Feb 14 20:35:27 2002 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.zcml:1.5 Message-ID: <200202150135.g1F1ZRj17659@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv17652 Modified Files: Contact.zcml Log Message: Copied from latest slides. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/Contact.zcml 1.4 => 1.5 === + + xmlns:browser='http://namespaces.zope.org/browser'> - - - - + + + + - + - + - - - - - - - - - - - - - - - - - - + + + + + + + + + - + - + + + + + + + + + - - From jim at zope.com Thu Feb 14 20:39:00 2002 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - ContactViewPresentation.py:1.4 Message-ID: <200202150139.g1F1d0t18802@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv18778 Modified Files: ContactViewPresentation.py Log Message: Changed to reflect new jargon. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactViewPresentation.py 1.3 => 1.4 === from IContactInfo import IContactInfo -# Create a simple view presentation -ContactViewPresentation = SimpleViewClass( - 'view.pt', used_for=IContactInfo) +# Create a simple info view +ContactViewView = SimpleViewClass( + 'info.pt', + used_for=IContactInfo) From jim at zope.com Thu Feb 14 20:39:43 2002 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - edit.pt:1.4 Message-ID: <200202150139.g1F1dhI18877@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv18870 Modified Files: edit.pt Log Message: Changed to use standard_macros. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/edit.pt 1.3 => 1.4 === + Edit contact -
+
Enter the information about the contact.
From jim at zope.com Thu Feb 14 20:40:17 2002 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - stubpostal.py:1.5 Message-ID: <200202150140.g1F1eH219000@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv18993 Modified Files: stubpostal.py Log Message: Changed to agree with slides. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/stubpostal.py 1.4 => 1.5 === from IPostal import IPostalLookup, IPostalInfo -class info: +class Info: __implements__ = IPostalInfo @@ -12,7 +12,7 @@ def state(self): return self._state -class lookup: +class Lookup: __implements__ = IPostalLookup @@ -24,7 +24,7 @@ def lookup(self, postal_code): data=self._data.get(postal_code) - if data is not None: data = info(*data) + if data is not None: data = Info(*data) return data -theLookup=lookup() +lookup=Lookup() From jim at zope.com Thu Feb 14 20:41:49 2002 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - ContactEditView.py:1.1 ContactInfoView.py:1.1 info.pt:1.1 ContactEditPresentation.py:NONE Message-ID: <200202150141.g1F1fnn19109@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv19049 Added Files: ContactEditView.py ContactInfoView.py info.pt Removed Files: ContactEditPresentation.py Log Message: Added info view (replacing view presentation). Renamed ContactEditPresentation to ContactEditView. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactEditView.py === from Zope.Publisher.Browser.AttributePublisher import AttributePublisher from Zope.PageTemplate import PageTemplateFile from IContactEdit import IContactEdit class ContactEditView(AttributePublisher): """Provide an interface for editing a contact """ # Boiler plate def __init__(self, context): self._context=context def getContext(self): return self._context # Assert that we can only be applied to IContactEdit __used_for__=IContactEdit # Input form index = PageTemplateFile('edit.pt', globals()) # action method def action(self, first, last, email, address, pc, REQUEST): "Edit a contact" self.getContext().update(first, last, email, address, pc) return self.index(REQUEST) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactInfoView.py === from Zope.PageTemplate import SimpleViewClass from IContactInfo import IContactInfo # Create a simple info view ContactInfoView = SimpleViewClass( 'info.pt', used_for=IContactInfo) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/info.pt === Contact Information
Contact information
Name: First Last
Address: Mailing address here
Email foo@bar.com
=== Removed File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactEditPresentation.py === From steve at cat-box.net Sat Feb 16 08:35:04 2002 From: steve at cat-box.net (Steve Alexander) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - contact_product_uml.txt:1.1 Message-ID: <200202161335.g1GDZ4n14654@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv14531 Added Files: contact_product_uml.txt Log Message: Added an ascii-art UML diagram of the Contact product. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/contact_product_uml.txt === <> o IContact <> | ----------- | | edit.pt | ----------- ----------- <> | Contact | | ----------- ----------- ^ | info.pt | | | V ----------- | | ------------------- | | o<- - - - - - - -| ContactEditView | | | IContactEdit ------------------- | | ^ | V |IContactInfo ------------------- o<- - - - - - - - - - - - - - - - - - -| ContactInfoView | ^ ------------------- : : o IPostalInfo : | : | -------------------- | ContactCityState | -------------------- : : <> + - - - - - ->o IPostalLookup | | | ---------- | Lookup | ---------- From steve at cat-box.net Sat Feb 16 08:41:46 2002 From: steve at cat-box.net (Steve Alexander) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - contact_product_uml.txt:1.2 Message-ID: <200202161341.g1GDfkw16028@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv16021 Modified Files: contact_product_uml.txt Log Message: Added a key to explain how the ascii symbols map onto UML pictures. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/contact_product_uml.txt 1.1 => 1.2 === ---------- + +Key: + + o Interface (catalysis style) + + ^ + V Aggregation + + + <- - - + Dependency + : + + <> Stereotype + + -------- + | name | Class + -------- + From steve at cat-box.net Sat Feb 16 09:26:15 2002 From: steve at cat-box.net (Steve Alexander) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - contact_product_uml.txt:1.3 Message-ID: <200202161426.g1GEQFe26766@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv26698 Modified Files: contact_product_uml.txt Log Message: Added some more stereotypes, to better explain the roles of the components. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/contact_product_uml.txt 1.2 => 1.3 === | Contact | | ----------- ----------- ^ | info.pt | - | | V ----------- - | | ------------------- | - | o<- - - - - - - -| ContactEditView | | - | IContactEdit ------------------- | - | ^ - | V + | | V <> ----------- + | | ------------------- | + | o<- - - - - - - -| ContactEditView | | + | IContactEdit ------------------- | + | ^ + | <> V |IContactInfo ------------------- o<- - - - - - - - - - - - - - - - - - -| ContactInfoView | ^ ------------------- : : o IPostalInfo : | - : | + : <> -------------------- | ContactCityState | -------------------- : - : <> + : + - - - - - ->o IPostalLookup | | - | + <> ---------- | Lookup | ---------- From steve at cat-box.net Sat Feb 16 09:30:37 2002 From: steve at cat-box.net (Steve Alexander) Date: Sun Aug 10 16:40:36 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - contact_product_uml.txt:1.4 Message-ID: <200202161430.g1GEUbN27937@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv27926 Modified Files: contact_product_uml.txt Log Message: Made aggregation slightly less ugly. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/contact_product_uml.txt 1.3 => 1.4 === ----------- ----------- <> | Contact | | ----------- - ----------- ^ | info.pt | - | | V <> ----------- - | | ------------------- | + ----------- | | info.pt | + | | ^ <> ----------- + | | --V---------------- | | o<- - - - - - - -| ContactEditView | | | IContactEdit ------------------- | - | ^ - | <> V - |IContactInfo ------------------- + | | + | <> ^ + |IContactInfo ----------V-------- o<- - - - - - - - - - - - - - - - - - -| ContactInfoView | ^ ------------------- : From steve at cat-box.net Sat Feb 16 12:33:44 2002 From: steve at cat-box.net (Steve Alexander) Date: Sun Aug 10 16:40:36 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - Contact.zcml:1.6 Message-ID: <200202161733.g1GHXin07079@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv6501 Modified Files: Contact.zcml Log Message: Added a missing "." to the end of the adapter declaration, which was making it get registered with the IToIRegistry as a module rather than an Interface. This stopped the adapter from working. I guess the zcml parsing machinery, or perhaps the IToIRegistry should check that it has an interface for the "for" attribute! === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/Contact.zcml 1.5 => 1.6 === factory=".Contact.ContactCityState." provides=".Contact.IPostal.IPostalInfo" - for=".Contact.IContactInfo" /> + for=".Contact.IContactInfo." /> From steve at cat-box.net Fri Feb 22 16:20:39 2002 From: steve at cat-box.net (Steve Alexander) Date: Sun Aug 10 16:40:36 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6 - New directory Message-ID: <200202222120.g1MLKdR30999@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6 In directory cvs.zope.org:/tmp/cvs-serv30993/Step6 Log Message: Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6 added to the repository === Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6 === From steve at cat-box.net Fri Feb 22 16:22:19 2002 From: steve at cat-box.net (Steve Alexander) Date: Sun Aug 10 16:40:36 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6 - Contact.py:1.1 Contact.zcml:1.1 ContactCityState.py:1.1 ContactEditView.py:1.1 ContactInfoView.py:1.1 IContact.py:1.1 IContactEdit.py:1.1 IContactInfo.py:1.1 IPostal.py:1.1 README.txt:1.1 __init__.py:1.1 contact.gif:1.1 contact_product_uml.txt:1.1 edit.pt:1.1 info.pt:1.1 products.zcml:1.1 stubpostal.py:1.1 Message-ID: <200202222122.g1MLMJt31202@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6 In directory cvs.zope.org:/tmp/cvs-serv31143/Step6 Added Files: Contact.py Contact.zcml ContactCityState.py ContactEditView.py ContactInfoView.py IContact.py IContactEdit.py IContactInfo.py IPostal.py README.txt __init__.py contact.gif contact_product_uml.txt edit.pt info.pt products.zcml stubpostal.py Log Message: Added Step6 to Chapter1 of the tutorial. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/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/Step6/Contact.zcml === === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/ContactCityState.py === from IPostal import IPostalLookup, IPostalInfo from IContactInfo import IContactInfo from Zope.ComponentArchitecture import getUtility class ContactCityState: "Provide access to city and state information for a contact" __implements__=IPostalInfo __used_for__=IContactInfo def __init__(self, contact): self._contact=contact lookup = getUtility(contact, IPostalLookup) 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/Step6/ContactEditView.py === from Zope.Publisher.Browser.AttributePublisher import AttributePublisher from Zope.PageTemplate import PageTemplateFile from IContactEdit import IContactEdit class ContactEditView(AttributePublisher): """Provide an interface for editing a contact """ # Boiler plate def __init__(self, context): self._context=context def getContext(self): return self._context # Assert that we can only be applied to IContactEdit __used_for__=IContactEdit # Input form index = PageTemplateFile('edit.pt', globals()) # action method def action(self, first, last, email, address, pc, REQUEST): "Edit a contact" self.getContext().update(first, last, email, address, pc) return self.index(REQUEST) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/ContactInfoView.py === from Zope.Publisher.Browser.AttributePublisher import AttributePublisher from Zope.PageTemplate import PageTemplateFile from IContactInfo import IContactInfo from Zope.ComponentArchitecture import getAdapter from IPostal import IPostalInfo class ContactInfoView(AttributePublisher): """Provide an interface for viewing a contact """ # Boiler plate def __init__(self, context): self._context=context info = getAdapter(context, IPostalInfo, None) self._info=info def getContext(self): return self._context # Do we need this? #__implements__=AttributePublisher.__implements__,IPostalInfo # Assert that we can only be applied to IContactInfo __used_for__=IContactInfo # Input form index = PageTemplateFile('info.pt', globals()) def city(self): return self._info.city() def state(self): return self._info.state() === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/IContact.py === from Interface import Interface, implements # Local Package imports from Contact import Contact class IContact(Interface): "Marker for objects that provide specific behavior" implements(Contact, IContact) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/IContactEdit.py === from IContactInfo import IContactInfo from Contact import Contact from Interface import implements class IContactEdit(IContactInfo): "Provides the ability to change basic contact information." def update(first, last, email, address, pc): """Modifies contact data 'None' values are ignored. """ implements(Contact, IContactEdit) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/IContactInfo.py === from Interface import Interface, implements # Local Package imports from Contact import Contact class IContactInfo(Interface): "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. """ implements(Contact, IContactInfo) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/IPostal.py === from Interface import Interface class IPostalInfo(Interface): "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 IPostalLookup(Interface): "Provide postal code lookup" def lookup(postal_code): """Lookup information for a postal code. An IPostalInfo is returned if the postal code is known. None is returned otherwise. """ === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/README.txt === Step6, making a composite view In the last step, we created a utility IPostalLookup to lookup city and state information in exchange for an object implementing IContactInfo. We also registered an adapter to perform this lookup when required. In this step, we'll be extending ContactInfoView to present the additional postal information. First, we need replace the simple info view we have in ContactInfoView.py for one we can extend. Here's our existing code:: from Zope.PageTemplate import SimpleViewClass from IContactInfo import IContactInfo # Create a simple info view ContactInfoView = SimpleViewClass( 'info.pt', used_for=IContactInfo) Instead of having SimpleViewClass do the work, we need to be a bit more verbose:: from Zope.Publisher.Browser.AttributePublisher import AttributePublisher from Zope.PageTemplate import PageTemplateFile from IContactInfo import IContactInfo class ContactInfoView(AttributePublisher): """Provide an interface for viewing a contact """ # Boiler plate def __init__(self, context): self._context=context def getContext(self): return self._context # Assert that we can only be applied to IContactInfo __used_for__=IContactInfo # Input form index = PageTemplateFile('info.pt', globals()) You'll notice that this follows the same form as ContactEditView.py. As we've now got an explicit 'index' method, we need to alter Contact.zcml to declare that 'index' is public: Change this:: to this:: We are now in an equivalent position to just before the start of this step. However, we're now ready to add fields for city and state to the view. First, we'll alter info.pt to add table rows for city and state:: Email: foo@bar.com City: City State: State We've allowed for default values so things still work while we continue with this step. We're getting the city and state information from the "container" namespace. This means to get it from the current view, which is ContactInfoView. Next, we'll alter ContactInfoView to get the city and state information and make this available to info.pt. We could declare that ContactInfoView now implements IPostalInfo. Although the only thing that needs to know this is info.pt, we're going to declare that ContactInfoView declares the interface so that we can easily make the security declarations. The new lines are prefixed with #NEW: comments:: from Zope.Publisher.Browser.AttributePublisher import AttributePublisher from Zope.PageTemplate import PageTemplateFile from IContactInfo import IContactInfo # new imports from Zope.ComponentArchitecture import getAdapter from IPostal import IPostalInfo class ContactInfoView(AttributePublisher): """Provide an interface for viewing a contact """ def __init__(self, context): self._context=context #NEW get an adapter info = getAdapter(context, IPostalInfo, None) #NEW save the adapter for use later during this request self._info=info def getContext(self): return self._context # Assert that we can only be applied to IContactInfo __used_for__=IContactInfo #NEW say that we implement IPostalInfo # we make sure to still advertise that we implement what # AttributePublisher implements. __implements__=AttributePublisher.__implements__, IPostalInfo # Input form index = PageTemplateFile('info.pt', globals()) #NEW provide city and state information def city(self): return self._info.city() def state(self): return self._info.state() We need to declare in Contact.zcml that the methods city and state of ContactInfoView are protected by the Zope.View permission. We'll do this using the IPostalInfo interface. Change these lines of Contact.zcml:: to this:: That's it. When you view a Contact object, it will now show you the city and state information for the contact's postcode. Of course, we're still using stubpostal.py, so you won't see anything for most postcodes. Good ones to try are 22401, 44870 and 90051. Take a look at contact_product_uml.txt for this Step for a picture of the interaction between classes at the end of this step. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/__init__.py === # empty __init__.py to make this into a package === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/contact.gif === === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/contact_product_uml.txt === <> o IContact <> | ----------- | | edit.pt | ----------- ----------- <> | Contact | | ----------- ----------- | | info.pt | | | ^ <> ----------- | | --V---------------- | | o<- - - - - - - -| ContactEditView | | | IContactEdit ------------------- | | | | <> ^ |IContactInfo ----------V-------- o<- - - - - - - - - - - - - - - - - - -| ContactInfoView | ^ ------------------- : : : o IPostalInfo : <> : | : : <> : -------------------- : | ContactCityState | : -------------------- : : : : v + - - - - - - - - - - - - - ->o IPostalLookup <> | | <> ---------- | Lookup | ---------- Key: o Interface (catalysis style) ^ V Aggregation <- - - + Dependency : <> Stereotype -------- | name | Class -------- === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/edit.pt === Edit contact
Enter the information about the contact.
First name
Last name
Email
Address
Postal Code
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/info.pt === Contact Information
Contact information
Name: First Last
Address: Mailing address here
Email: foo@bar.com
City: City
State: State
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/products.zcml === === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step6/stubpostal.py === # Stub postal utility implemantation from IPostal import IPostalLookup, IPostalInfo class Info: __implements__ = IPostalInfo 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__ = IPostalLookup _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 lookup=Lookup()