Merge lp:~leonardr/lazr.restful/test-multiversion-collection into lp:lazr.restful

Proposed by Leonard Richardson
Status: Merged
Merged at revision: not available
Proposed branch: lp:~leonardr/lazr.restful/test-multiversion-collection
Merge into: lp:lazr.restful
Diff against target: 298 lines (+90/-43)
1 file modified
src/lazr/restful/docs/multiversion.txt (+90/-43)
To merge this branch: bzr merge lp:~leonardr/lazr.restful/test-multiversion-collection
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Review via email: mp+18089@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Leonard Richardson (leonardr) wrote :

This branch changes the multiversion.txt doctest so that there are two different implementations of the 'contact' collection; one used in the 'beta' and '1.0' versions, one used in the 'dev' version. There is no new code; the test merely illustrates something that was already possible.

I also changed the overly long names of the marker interfaces used to designate different versions of the web service. IWebServiceRequestBeta now becomes simply IBetaVersion, and so on.

Revision history for this message
Graham Binns (gmb) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/lazr/restful/docs/multiversion.txt'
2--- src/lazr/restful/docs/multiversion.txt 2010-01-12 15:06:15 +0000
3+++ src/lazr/restful/docs/multiversion.txt 2010-01-26 17:10:25 +0000
4@@ -87,7 +87,7 @@
5 Every version must have a corresponding subclass of
6 IWebServiceClientRequest. Each interface class is registered as a
7 named utility implementing IWebServiceVersion. For instance, in the
8-example below, the IWebServiceRequest10 class will be registered as
9+example below, the I10Version class will be registered as
10 the IWebServiceVersion utility with the name "1.0".
11
12 When a request comes in, lazr.restful figures out which version the
13@@ -99,18 +99,18 @@
14 registered automatically.
15
16 >>> from lazr.restful.interfaces import IWebServiceClientRequest
17- >>> class IWebServiceRequestBeta(IWebServiceClientRequest):
18- ... pass
19-
20- >>> class IWebServiceRequest10(IWebServiceClientRequest):
21- ... pass
22-
23- >>> class IWebServiceRequestDev(IWebServiceClientRequest):
24- ... pass
25-
26- >>> versions = ((IWebServiceRequestBeta, 'beta'),
27- ... (IWebServiceRequest10, '1.0'),
28- ... (IWebServiceRequestDev, 'dev'))
29+ >>> class IBetaVersion(IWebServiceClientRequest):
30+ ... pass
31+
32+ >>> class I10Version(IWebServiceClientRequest):
33+ ... pass
34+
35+ >>> class IDevVersion(IWebServiceClientRequest):
36+ ... pass
37+
38+ >>> versions = ((IBetaVersion, 'beta'),
39+ ... (I10Version, '1.0'),
40+ ... (IDevVersion, 'dev'))
41
42 >>> from lazr.restful import register_versioned_request_utility
43 >>> for cls, version in versions:
44@@ -142,6 +142,9 @@
45 ... def getAllContacts():
46 ... "Get all contacts."
47 ...
48+ ... def getContactsWithPhone():
49+ ... "Get all contacts that have a phone number."
50+ ...
51 ... def findContacts(self, string, search_fax):
52 ... """Find contacts by name, phone number, or fax number."""
53
54@@ -168,6 +171,7 @@
55
56 Here's a simple ContactSet with a predefined list of contacts.
57
58+ >>> from operator import attrgetter
59 >>> from zope.publisher.interfaces.browser import IBrowserRequest
60 >>> from lazr.restful.interfaces import IServiceRootResource
61 >>> from lazr.restful.simple import TraverseWithGet
62@@ -188,10 +192,15 @@
63 ... def getAllContacts(self):
64 ... return self.contacts
65 ...
66+ ... def getContactsWithPhone(self):
67+ ... return [contact for contact in self.contacts
68+ ... if contact.phone is not None]
69+ ...
70 ... def findContacts(self, string, search_fax=True):
71 ... return [contact for contact in self.contacts
72 ... if (string in contact.name
73- ... or string in contact.phone
74+ ... or (contact.phone is not None
75+ ... and string in contact.phone)
76 ... or (search_fax and string in contact.fax))]
77 ...
78 ... @property
79@@ -214,7 +223,8 @@
80
81 >>> C1 = Contact("Cleo Python", "555-1212", "111-2121")
82 >>> C2 = Contact("Oliver Bluth", "10-1000000", "22-2222222")
83- >>> CONTACTS = [C1, C2]
84+ >>> C3 = Contact("Fax-your-order Pizza", None, "100-200-300")
85+ >>> CONTACTS = [C1, C2, C3]
86
87 Defining the web service data model
88 ===================================
89@@ -289,7 +299,7 @@
90 ... self.context = context
91
92 >>> sm.registerAdapter(
93- ... ContactEntryBeta, [IContact, IWebServiceRequestBeta],
94+ ... ContactEntryBeta, [IContact, IBetaVersion],
95 ... provided=IContactEntry)
96
97 By wrapping one of our predefined Contacts in a ContactEntryBeta
98@@ -320,7 +330,7 @@
99 ... def fax_number(self):
100 ... return self.context.fax
101 >>> sm.registerAdapter(
102- ... ContactEntry10, [IContact, IWebServiceRequest10],
103+ ... ContactEntry10, [IContact, I10Version],
104 ... provided=IContactEntry)
105
106 >>> entry = ContactEntry10(C1, None)
107@@ -343,7 +353,7 @@
108 ... def phone_number(self):
109 ... return self.context.phone
110 >>> sm.registerAdapter(
111- ... ContactEntryDev, [IContact, IWebServiceRequestDev],
112+ ... ContactEntryDev, [IContact, IDevVersion],
113 ... provided=IContactEntry)
114
115 >>> entry = ContactEntryDev(C1, None)
116@@ -378,19 +388,19 @@
117
118 >>> from zope.component import getMultiAdapter
119 >>> request_beta = create_web_service_request('/beta/')
120- >>> alsoProvides(request_beta, IWebServiceRequestBeta)
121+ >>> alsoProvides(request_beta, IBetaVersion)
122 >>> beta_entry = getMultiAdapter((C1, request_beta), IEntry)
123 >>> print beta_entry.fax
124 111-2121
125
126 >>> request_10 = create_web_service_request('/1.0/')
127- >>> alsoProvides(request_10, IWebServiceRequest10)
128+ >>> alsoProvides(request_10, I10Version)
129 >>> one_oh_entry = getMultiAdapter((C1, request_10), IEntry)
130 >>> print one_oh_entry.fax_number
131 111-2121
132
133 >>> request_dev = create_web_service_request('/dev/')
134- >>> alsoProvides(request_dev, IWebServiceRequestDev)
135+ >>> alsoProvides(request_dev, IDevVersion)
136 >>> dev_entry = getMultiAdapter((C1, request_dev), IEntry)
137 >>> print dev_entry.fax
138 Traceback (most recent call last):
139@@ -402,16 +412,20 @@
140
141 The set of contacts publishes a slightly different named operation in
142 every version of the web service, so in a little bit we'll be
143-implementing three different versions of the same named operation. But
144-the contact set itself doesn't change between versions (although it
145-could). So it's sufficient to implement one ICollection implementation
146-and register it as the implementation for every version of the web
147-service.
148+implementing three different versions of the same named operation. The
149+contact set itself also changes between versions. In the 'beta' and
150+'1.0' versions, the contact set serves all contcts. In the 'dev'
151+version, the contact set omits contacts that only have a fax
152+number. We'll implement this behavior by implementing ICollection
153+twice and registering each implementation for the appropriate versions
154+of the web service.
155
156 >>> from lazr.restful import Collection
157 >>> from lazr.restful.interfaces import ICollection
158
159- >>> class ContactCollection(Collection):
160+First we'll implement the version used in 'beta' and '1.0'.
161+
162+ >>> class ContactCollectionBeta(Collection):
163 ... """A collection of contacts, exposed through the web service."""
164 ... adapts(IContactSet)
165 ...
166@@ -425,24 +439,55 @@
167
168 >>> from zope.interface.verify import verifyObject
169 >>> contact_set = ContactSet()
170- >>> verifyObject(ICollection, ContactCollection(contact_set, None))
171+ >>> verifyObject(ICollection, ContactCollectionBeta(contact_set, None))
172 True
173
174-Register it as the ICollection adapter for IContactSet. We use a
175-generic request interface (IWebServiceClientRequest) rather than a
176-specific one like IWebServiceRequestBeta, so that the same
177-implementation will be used for every version of the web service.
178+Register it as the ICollection adapter for IContactSet in
179+IBetaVersion and I10Version.
180
181- >>> sm.registerAdapter(
182- ... ContactCollection, [IContactSet, IWebServiceClientRequest],
183- ... provided=ICollection)
184+ >>> for version in [IBetaVersion, I10Version]:
185+ ... sm.registerAdapter(
186+ ... ContactCollectionBeta, [IContactSet, version],
187+ ... provided=ICollection)
188
189 Make sure the functionality works properly.
190
191 >>> collection = getMultiAdapter(
192 ... (contact_set, request_beta), ICollection)
193 >>> len(collection.find())
194- 2
195+ 3
196+
197+ >>> collection = getMultiAdapter(
198+ ... (contact_set, request_10), ICollection)
199+ >>> len(collection.find())
200+ 3
201+
202+Now let's implement the different version used in 'dev'.
203+
204+ >>> class ContactCollectionDev(ContactCollectionBeta):
205+ ... def find(self):
206+ ... """Find all the contacts, sorted by name."""
207+ ... return self.context.getContactsWithPhone()
208+
209+This class also implements ICollection.
210+
211+ >>> verifyObject(ICollection, ContactCollectionDev(contact_set, None))
212+ True
213+
214+Register it as the ICollection adapter for IContactSet in
215+IDevVersion.
216+
217+ >>> sm.registerAdapter(
218+ ... ContactCollectionDev, [IContactSet, IDevVersion],
219+ ... provided=ICollection)
220+
221+Make sure the functionality works properly. Note that the contact that
222+only has a fax number no longer shows up.
223+
224+ >>> collection = getMultiAdapter(
225+ ... (contact_set, request_dev), ICollection)
226+ >>> [contact.name for contact in collection.find()]
227+ ['Cleo Python', 'Oliver Bluth']
228
229 Implementing the named operations
230 ---------------------------------
231@@ -479,11 +524,11 @@
232 'beta' service, and the 'find' operation in the '1.0' service.
233
234 >>> sm.registerAdapter(
235- ... FindContactsOperationBase, [IContactSet, IWebServiceRequestBeta],
236+ ... FindContactsOperationBase, [IContactSet, IBetaVersion],
237 ... provided=IResourceGETOperation, name="findContacts")
238
239 >>> sm.registerAdapter(
240- ... FindContactsOperationBase, [IContactSet, IWebServiceRequest10],
241+ ... FindContactsOperationBase, [IContactSet, I10Version],
242 ... provided=IResourceGETOperation, name="find")
243
244 Here's the slightly different named operation as implemented in
245@@ -500,7 +545,7 @@
246 ... return str(e)
247
248 >>> sm.registerAdapter(
249- ... FindContactsOperationNoFax, [IContactSet, IWebServiceRequestDev],
250+ ... FindContactsOperationNoFax, [IContactSet, IDevVersion],
251 ... provided=IResourceGETOperation, name="find")
252
253 The service root resource
254@@ -573,14 +618,14 @@
255 ... create_web_service_request)
256
257 >>> request_beta = create_web_service_request('/beta/')
258- >>> IWebServiceRequestBeta.providedBy(request_beta)
259+ >>> IBetaVersion.providedBy(request_beta)
260 False
261
262 The traversal process associates the request with a particular version.
263
264 >>> request_beta.traverse(None)
265 <BetaServiceRootResource object ...>
266- >>> IWebServiceRequestBeta.providedBy(request_beta)
267+ >>> IBetaVersion.providedBy(request_beta)
268 True
269 >>> print request_beta.version
270 beta
271@@ -620,11 +665,12 @@
272
273 >>> body = simplejson.loads(resource())
274 >>> body['total_size']
275- 2
276+ 3
277 >>> for link in sorted(
278 ... [contact['self_link'] for contact in body['entries']]):
279 ... print link
280 http://api.multiversion.dev/beta/contact_list/Cleo%20Python
281+ http://api.multiversion.dev/beta/contact_list/Fax-your-order%20Pizza
282 http://api.multiversion.dev/beta/contact_list/Oliver%20Bluth
283
284 We can traverse through the collection to an entry.
285@@ -713,11 +759,12 @@
286
287 >>> body = simplejson.loads(resource())
288 >>> body['total_size']
289- 2
290+ 3
291 >>> for link in sorted(
292 ... [contact['self_link'] for contact in body['entries']]):
293 ... print link
294 http://api.multiversion.dev/1.0/contacts/Cleo%20Python
295+ http://api.multiversion.dev/1.0/contacts/Fax-your-order%20Pizza
296 http://api.multiversion.dev/1.0/contacts/Oliver%20Bluth
297
298 We can traverse through the collection to an entry.

Subscribers

People subscribed via source and target branches