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

Proposed by Leonard Richardson on 2010-01-26
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 2010-01-26 Approve on 2010-01-26
Review via email: mp+18089@code.launchpad.net
To post a comment you must log in.
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.

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