[Zope3-checkins] CVS: Zope3/src/buddydemo - README.txt:1.1 __init__.py:1.1 browser.py:1.1 buddy.py:1.1 configure.zcml:1.1 ftests.py:1.1 info.pt:1.1 interfaces.py:1.1 rename.pt:1.1 stubpostal.py:1.1 tests.py:1.1

Jim Fulton jim at zope.com
Wed Mar 24 15:05:52 EST 2004


Update of /cvs-repository/Zope3/src/buddydemo
In directory cvs.zope.org:/tmp/cvs-serv9205/src/buddydemo

Added Files:
	README.txt __init__.py browser.py buddy.py configure.zcml 
	ftests.py info.pt interfaces.py rename.pt stubpostal.py 
	tests.py 
Log Message:
Checked in source code for the short programmer's tutorial.

I checked this in as a top-level package, because that's how the demo
wants to be installed.  This will probably be appropriate for lots of
demo apps.


=== Added File Zope3/src/buddydemo/README.txt ===
==========
Buddy Demo
==========

This package demonstrates a very simple content-type implementation in
Zope 3.  It provides the sample code for the example developed in the
short version of the programmer's tutorial:

  http://dev.zope.org/Zope3/ShortTutorial

and 

  
  http://cvs.zope.org/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/short.sxi


=== Added File Zope3/src/buddydemo/__init__.py ===
#


=== Added File Zope3/src/buddydemo/browser.py ===
import zope.interface
from buddydemo.interfaces import IBuddy, IPostalInfo
from zope.app.event import publish
from zope.app.event.objectevent import ObjectModifiedEvent

class BuddyInfo:
    """Provide an interface for viewing a Buddy
    """

    zope.interface.implements(IPostalInfo)
    __used_for__ = IBuddy

    def __init__(self, context, request):
        self.context = context
        self.request = request
        
        info = IPostalInfo(context)
        self.city, self.state = info.city, info.state

class BuddyRename:
    """Rename a buddy"""

    def __init__(self, context, request):
        self.context = context
        self.request = request

    def update(self, first, last):
        self.context.first = first
        self.context.last = last
        publish(self.context, ObjectModifiedEvent(self.context))
        self.request.response.redirect("rename.html")

        


=== Added File Zope3/src/buddydemo/buddy.py ===
import persistent
import zope.interface
from buddydemo.interfaces import IBuddy
from buddydemo.interfaces import IPostalLookup, IPostalInfo
from buddydemo.interfaces import IBuddyContained
from zope.app import zapi

class Buddy(persistent.Persistent):
    """Buddy information

    >>> bud = Buddy('Bob', 'Smith', 'bob at smith.org',
    ...             '513 Princess Ann Street', '22401')
    >>> bud.first, bud.last, bud.email
    ('Bob', 'Smith', 'bob at smith.org')
    >>> bud.address, bud.postal_code
    ('513 Princess Ann Street', '22401')

    Any data not passed to the class are initialized to
    empty strings:

    >>> bud = Buddy()
    >>> bud.first, bud.last, bud.email
    ('', '', '')
    >>> bud.address, bud.postal_code
    ('', '')
    """

    zope.interface.implements(IBuddy, IBuddyContained)

    __parent__ = __name__ = None

    def __init__(self, first='', last='', email='',
                 address='', pc=''):
        self.first, self.last = first, last
        self.email = email
        self.address, self.postal_code = address, pc

    def name(self):
        """Get the full name of a buddy

        >>> bud = Buddy('Bob', 'Smith')
        >>> bud.name()
        'Bob Smith'
        """
        return "%s %s" % (self.first, self.last)

from zope.app.container.btree import BTreeContainer
from buddydemo.interfaces import IBuddyFolder

class BuddyFolder(BTreeContainer):
    zope.interface.implements(IBuddyFolder)




class BuddyCityState:
    """Provide city and state information for a buddy

    The adapter needs a postal-lookup utility.  For the
    sake of the example, we'll install one, but first,
    we have to set up the component architecture:

      >>> from zope.app.tests import placelesssetup, ztapi
      >>> placelesssetup.setUp()

    and then we can provide the utility:

      >>> from stubpostal import Lookup
      >>> ztapi.provideUtility(IPostalLookup, Lookup())

    Now, we can try our adapter.  If we have no postal
    code, we get no data:

      >>> bob = Buddy('Bob')
      >>> info = BuddyCityState(bob)
      >>> info.city, info.state
      ('', '')

    But if we use a known postal code:

      >>> bob = Buddy('Bob', '', '', '', '22401')
      >>> info = BuddyCityState(bob)
      >>> info.city, info.state
      ('Fredericksburg', 'Virginia')

    Finally, we'll put things back the way we found them:

      >>> placelesssetup.tearDown()

    """

    zope.interface.implements(IPostalInfo)

    __used_for__ = IBuddy

    def __init__(self, buddy):
        lookup = zapi.getUtility(buddy, IPostalLookup)
        info = lookup.lookup(buddy.postal_code)
        if info is None:
            self.city, self.state = '', ''
        else:
            self.city, self.state = info.city, info.state
        


=== Added File Zope3/src/buddydemo/configure.zcml ===
<configure 
    xmlns='http://namespaces.zope.org/zope'
    xmlns:browser='http://namespaces.zope.org/browser'
    i18n_domain="buddydemo"
    xmlns:i18n="http://namespaces.zope.org/i18n"
    >

<i18n:registerTranslations directory="locales" />

<content class=".buddy.Buddy">
   <implements 
       interface="zope.app.annotation.IAttributeAnnotatable" />
   <require permission="zope.View"
            interface=".interfaces.IBuddy" />
   <require permission="zope.ManageContent"
            set_schema=".interfaces.IBuddy" />
</content>

<browser:page
    for=".interfaces.IBuddy"
    name="index.html"
    template="info.pt"
    permission="zope.View"
    class=".browser.BuddyInfo"
    />

<browser:editform
    schema=".interfaces.IBuddy"
    label="Change Buddy Information"    
    name="edit.html"       
    menu="zmi_views" title="Edit"
    permission="zope.ManageContent" 
    />

<browser:addform
    schema=".interfaces.IBuddy"
    label="Add buddy information"
    content_factory=".buddy.Buddy"
    arguments="first last email address postal_code"
    name="AddBuddy.html"
    permission="zope.ManageContent" 
    />

<browser:addMenuItem
    class=".buddy.Buddy"
    title="Buddy"
    permission="zope.ManageContent" 
    view="AddBuddy.html"
    />

<content class=".stubpostal.Info">
  <allow interface=".interfaces.IPostalInfo" />
</content>

<utility
    factory=".stubpostal.Lookup" 
    provides=".interfaces.IPostalLookup"
    permission="zope.Public" 
    />

<adapter
    factory=".buddy.BuddyCityState"
    provides=".interfaces.IPostalInfo" 
    for=".interfaces.IBuddy"
    permission="zope.Public" 
    />

<browser:pages
    for=".interfaces.IBuddy"
    permission="zope.ManageContent"
    class=".browser.BuddyRename"
    >
  <browser:page
      name="rename.html"
      menu="zmi_views" title="Rename"
      template="rename.pt"
      />
  <browser:page
      name="renameAction.html"
      attribute="update"
      />
</browser:pages>

<content class=".buddy.BuddyFolder">
  <require permission="zope.View" 
           interface="zope.app.container.interfaces.IReadContainer"
           />
  <require permission="zope.ManageContent" 
         interface="zope.app.container.interfaces.IWriteContainer"
           />
</content>

<browser:addMenuItem
    title="Buddy Folder" 
    class=".buddy.BuddyFolder"
    permission="zope.ManageContent"
    />

<browser:containerViews
     for=".interfaces.IBuddyFolder"
     contents="zope.ManageContent"
     index="zope.View"
     add="zope.ManageContent"
     />

</configure> 


=== Added File Zope3/src/buddydemo/ftests.py ===
import unittest
from buddydemo.buddy import Buddy
from zope.testing.functional import BrowserTestCase

class Test(BrowserTestCase):

    def setUp(self):
        BrowserTestCase.setUp(self)
        self.bob = Buddy('Bob', 'Smith', 'bob at smith.org',
                    '513 Princess Ann Street', '22401')
        self.getRootFolder()['bob'] = self.bob
        self.commit()
    
    def test_buddy_display(self):
        response = self.publish('/bob')
        body = response.getBody()
        for name in ('first', 'last', 'email',
                     'address', 'postal_code'):
            self.assert_(getattr(self.bob, name) in body)
        self.assert_('Fredericksburg' in body)
        self.assert_('Virginia' in body)

    def test_buddy_edit(self):
        response = self.publish('/bob/edit.html', basic='mgr:mgrpw')
        self.assertEqual(response.getStatus(), 200)

    def test_buddy_add(self):
        response = self.publish('/+/AddBuddy.html', basic='mgr:mgrpw')
        self.assertEqual(response.getStatus(), 200)
        
def test_suite():
    return unittest.makeSuite(Test)


=== Added File Zope3/src/buddydemo/info.pt ===
<html metal:use-macro="context/@@standard_macros/page"
      i18n:domain="buddydemo">
<body><div metal:fill-slot="body">
  <table>
    <caption i18n:translate="">Buddy information</caption>
    <tr><td i18n:translate="">Name:</td>
        <td><span tal:replace="context/first">First</span>
            <span tal:replace="context/last">Last</span></td>
        </tr>
    <tr><td i18n:translate="">Email:</td>
        <td tal:content="context/email">foo at bar.com</td>
        </tr>
    <tr><td i18n:translate="">Address:</td>
        <td tal:content="context/address">1 First Street</td>
        </tr>
    <tr><td>City:</td>
        <td tal:content="view/city | default">City</td>
        </tr>
    <tr><td>State:</td>
        <td tal:content="view/state | default">State</td>
        </tr>
    <tr><td i18n:translate="">Postal code:</td>
        <td tal:content="context/postal_code">12345</td>
        </tr>
  </table>
</div></body></html>


=== Added File Zope3/src/buddydemo/interfaces.py ===
import re
import zope.interface
from zope.schema import Text, TextLine

from zope.i18nmessageid import MessageIDFactory
_ = MessageIDFactory("buddydemo")

class IBuddy(zope.interface.Interface):
    """Provides access to basic buddy information"""

    first = TextLine(title=_("First name"))
    last = TextLine(title=_("Last name"))
    email = TextLine(title=_("Electronic mail address"))
    address = Text(title=_("Postal address"))
    postal_code = TextLine(title=_("Postal code"),
         constraint=re.compile("\d{5,5}(-\d{4,4})?$").match)

    def name():
        """Gets the buddy name.
        
        The buddy name is the first and last name"""



from zope.app.container.interfaces import IContained, IContainer
from zope.app.container.constraints import ContainerTypesConstraint
from zope.app.container.constraints import ItemTypePrecondition
from zope.schema import Field

class IBuddyFolder(IContainer):

    def __setitem__(name, object):
        """Add a buddy"""

    __setitem__.precondition = ItemTypePrecondition(IBuddy)

class IBuddyContained(IContained):
    __parent__ = Field(
             constraint = ContainerTypesConstraint(IBuddyFolder))




class IPostalInfo(zope.interface.Interface):
    "Provide information for postal codes"

    city = TextLine(title=u"City")
    state = TextLine(title=u"State")

class IPostalLookup(zope.interface.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 Zope3/src/buddydemo/rename.pt ===
<html metal:use-macro="context/@@standard_macros/page"
      i18n:domain="buddydemo">
<body>
<div metal:fill-slot="body">
<p i18n:translate="">Enter the Buddy information</p>
<form action="renameAction.html" method="post">
<table>
  <tr><td i18n:translate="">First name</td>
      <td><input type="text" name="first" size="40" value=""
           tal:attributes="value context/first" /> </td>
  </tr>
  <tr><td i18n:translate="">Last name</td>
      <td><input type="text" name="last" size="40" value=""
           tal:attributes="value context/last" /> </td>
  </tr>
</table>
<input type="submit" name="submit" value="Save Changes" />
</form></div></body></html>


=== Added File Zope3/src/buddydemo/stubpostal.py ===
import zope.interface
from buddydemo.interfaces import IPostalInfo, IPostalLookup

class Info:
    """Postal information

    >>> info = Info('Cleveland', 'Ohio')
    >>> info.city, info.state
    ('Cleveland', 'Ohio')
    """

    zope.interface.implements(IPostalInfo)

    def __init__(self, city, state):
        self.city, self.state = city, state

class Lookup:

    zope.interface.implements(IPostalLookup)
    
    _data = {
        '22401': ('Fredericksburg', 'Virginia'),
        '44870': ('Sandusky', 'Ohio'),
        '90051': ('Los Angeles', 'California'),
        }

    def lookup(self, postal_code):
        """Lookup city and state information

        If a known postal code is used, we get data:

          >>> lookup = Lookup()
          >>> info = lookup.lookup('22401')
          >>> info.city, info.state
          ('Fredericksburg', 'Virginia')

        But get nothing if an unknown code is given:

          >>> lookup.lookup('11111')
        """
        data = self._data.get(postal_code)
        if data:
            return Info(*data)


=== Added File Zope3/src/buddydemo/tests.py ===
import unittest
from doctest import DocTestSuite

def test_BuddyInfo():
    """
    This view mainly provides access to city and state
    information for a buddy.  It relies on having a
    buddy that is adaptable to IPostalInfo.  Imagine we
    have a buddy that already implements IPostalInfo:

      >>> import zope.interface
      >>> from buddydemo.interfaces import IPostalInfo
      >>> class FakeBuddy:
      ...     zope.interface.implements(IPostalInfo)
      ...     city = 'Cleveland'
      ...     state = 'Ohio'
      >>> fake = FakeBuddy()

    We should be able to create a BuddyInfo on this
    fake buddy and get the right city and state back:

      >>> from buddydemo.browser import BuddyInfo
      >>> info = BuddyInfo(fake, 42)
      >>> info.city, info.state
      ('Cleveland', 'Ohio')

    We cheated a bit and used 42 as the request.

    As with all views, we expect to be able to access
    the thing being viewed and the request:

      >>> info.context is fake
      True
      >>> info.request
      42
    """

def test_BuddyRename():
    r"""
    This view provides a method for changing buddies.
    It is the action of a form in rename.html and
    redirects back there when it's done.

    Use a fake buddy class:

      >>> import zope.interface
      >>> class FakeBuddy:
      ...     first = 'bob'
      ...     last = 'smoth'
      >>> fake = FakeBuddy()

    Because the view needs to redirect, we have to give
    it a request:

      >>> from zope.publisher.browser import TestRequest
      >>> request = TestRequest()

    Our rename view is going to generate an event. Because
    of that, we need to setup an event service:

      >>> from zope.app.tests import placelesssetup
      >>> placelesssetup.setUp()
      
    We should be able to create a BuddyRename on this
    fake buddy and change it's name:

      >>> from buddydemo.browser import BuddyRename
      >>> rename = BuddyRename(fake, request)
      >>> rename.update('Bob', 'Smith')
      >>> fake.first, fake.last
      ('Bob', 'Smith')

    Make sure it redirected to rename.html:
    
      >>> request.response.getStatus()
      302
      >>> request.response.getHeader('location')
      'rename.html'

    There should be an ObjectModifiedEvent event logged:

      >>> from zope.app.event.tests.placelesssetup \
      ...      import getEvents
      >>> from zope.app.event.interfaces \
      ...      import IObjectModifiedEvent
      >>> [event] = getEvents(IObjectModifiedEvent)
      >>> event.object is fake
      True

    Finally, we'll put things back the way we found them:

      >>> placelesssetup.tearDown()
    """

def test_suite():
    suite = unittest.TestSuite()
    suite.addTest(DocTestSuite('buddydemo.buddy'))
    suite.addTest(DocTestSuite('buddydemo.stubpostal'))
    suite.addTest(DocTestSuite())
    return suite




More information about the Zope3-Checkins mailing list