[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