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
=== modified file 'src/lazr/restful/docs/multiversion.txt'
--- src/lazr/restful/docs/multiversion.txt 2010-01-12 15:06:15 +0000
+++ src/lazr/restful/docs/multiversion.txt 2010-01-26 17:10:25 +0000
@@ -87,7 +87,7 @@
87Every version must have a corresponding subclass of87Every version must have a corresponding subclass of
88IWebServiceClientRequest. Each interface class is registered as a88IWebServiceClientRequest. Each interface class is registered as a
89named utility implementing IWebServiceVersion. For instance, in the89named utility implementing IWebServiceVersion. For instance, in the
90example below, the IWebServiceRequest10 class will be registered as90example below, the I10Version class will be registered as
91the IWebServiceVersion utility with the name "1.0".91the IWebServiceVersion utility with the name "1.0".
9292
93When a request comes in, lazr.restful figures out which version the93When a request comes in, lazr.restful figures out which version the
@@ -99,18 +99,18 @@
99registered automatically.99registered automatically.
100100
101 >>> from lazr.restful.interfaces import IWebServiceClientRequest101 >>> from lazr.restful.interfaces import IWebServiceClientRequest
102 >>> class IWebServiceRequestBeta(IWebServiceClientRequest):102 >>> class IBetaVersion(IWebServiceClientRequest):
103 ... pass103 ... pass
104104
105 >>> class IWebServiceRequest10(IWebServiceClientRequest):105 >>> class I10Version(IWebServiceClientRequest):
106 ... pass106 ... pass
107107
108 >>> class IWebServiceRequestDev(IWebServiceClientRequest):108 >>> class IDevVersion(IWebServiceClientRequest):
109 ... pass109 ... pass
110110
111 >>> versions = ((IWebServiceRequestBeta, 'beta'),111 >>> versions = ((IBetaVersion, 'beta'),
112 ... (IWebServiceRequest10, '1.0'),112 ... (I10Version, '1.0'),
113 ... (IWebServiceRequestDev, 'dev'))113 ... (IDevVersion, 'dev'))
114114
115 >>> from lazr.restful import register_versioned_request_utility115 >>> from lazr.restful import register_versioned_request_utility
116 >>> for cls, version in versions:116 >>> for cls, version in versions:
@@ -142,6 +142,9 @@
142 ... def getAllContacts():142 ... def getAllContacts():
143 ... "Get all contacts."143 ... "Get all contacts."
144 ...144 ...
145 ... def getContactsWithPhone():
146 ... "Get all contacts that have a phone number."
147 ...
145 ... def findContacts(self, string, search_fax):148 ... def findContacts(self, string, search_fax):
146 ... """Find contacts by name, phone number, or fax number."""149 ... """Find contacts by name, phone number, or fax number."""
147150
@@ -168,6 +171,7 @@
168171
169Here's a simple ContactSet with a predefined list of contacts.172Here's a simple ContactSet with a predefined list of contacts.
170173
174 >>> from operator import attrgetter
171 >>> from zope.publisher.interfaces.browser import IBrowserRequest175 >>> from zope.publisher.interfaces.browser import IBrowserRequest
172 >>> from lazr.restful.interfaces import IServiceRootResource176 >>> from lazr.restful.interfaces import IServiceRootResource
173 >>> from lazr.restful.simple import TraverseWithGet177 >>> from lazr.restful.simple import TraverseWithGet
@@ -188,10 +192,15 @@
188 ... def getAllContacts(self):192 ... def getAllContacts(self):
189 ... return self.contacts193 ... return self.contacts
190 ...194 ...
195 ... def getContactsWithPhone(self):
196 ... return [contact for contact in self.contacts
197 ... if contact.phone is not None]
198 ...
191 ... def findContacts(self, string, search_fax=True):199 ... def findContacts(self, string, search_fax=True):
192 ... return [contact for contact in self.contacts200 ... return [contact for contact in self.contacts
193 ... if (string in contact.name201 ... if (string in contact.name
194 ... or string in contact.phone202 ... or (contact.phone is not None
203 ... and string in contact.phone)
195 ... or (search_fax and string in contact.fax))]204 ... or (search_fax and string in contact.fax))]
196 ...205 ...
197 ... @property206 ... @property
@@ -214,7 +223,8 @@
214223
215 >>> C1 = Contact("Cleo Python", "555-1212", "111-2121")224 >>> C1 = Contact("Cleo Python", "555-1212", "111-2121")
216 >>> C2 = Contact("Oliver Bluth", "10-1000000", "22-2222222")225 >>> C2 = Contact("Oliver Bluth", "10-1000000", "22-2222222")
217 >>> CONTACTS = [C1, C2]226 >>> C3 = Contact("Fax-your-order Pizza", None, "100-200-300")
227 >>> CONTACTS = [C1, C2, C3]
218228
219Defining the web service data model229Defining the web service data model
220===================================230===================================
@@ -289,7 +299,7 @@
289 ... self.context = context299 ... self.context = context
290300
291 >>> sm.registerAdapter(301 >>> sm.registerAdapter(
292 ... ContactEntryBeta, [IContact, IWebServiceRequestBeta],302 ... ContactEntryBeta, [IContact, IBetaVersion],
293 ... provided=IContactEntry)303 ... provided=IContactEntry)
294304
295By wrapping one of our predefined Contacts in a ContactEntryBeta305By wrapping one of our predefined Contacts in a ContactEntryBeta
@@ -320,7 +330,7 @@
320 ... def fax_number(self):330 ... def fax_number(self):
321 ... return self.context.fax331 ... return self.context.fax
322 >>> sm.registerAdapter(332 >>> sm.registerAdapter(
323 ... ContactEntry10, [IContact, IWebServiceRequest10],333 ... ContactEntry10, [IContact, I10Version],
324 ... provided=IContactEntry)334 ... provided=IContactEntry)
325335
326 >>> entry = ContactEntry10(C1, None)336 >>> entry = ContactEntry10(C1, None)
@@ -343,7 +353,7 @@
343 ... def phone_number(self):353 ... def phone_number(self):
344 ... return self.context.phone354 ... return self.context.phone
345 >>> sm.registerAdapter(355 >>> sm.registerAdapter(
346 ... ContactEntryDev, [IContact, IWebServiceRequestDev],356 ... ContactEntryDev, [IContact, IDevVersion],
347 ... provided=IContactEntry)357 ... provided=IContactEntry)
348358
349 >>> entry = ContactEntryDev(C1, None)359 >>> entry = ContactEntryDev(C1, None)
@@ -378,19 +388,19 @@
378388
379 >>> from zope.component import getMultiAdapter389 >>> from zope.component import getMultiAdapter
380 >>> request_beta = create_web_service_request('/beta/')390 >>> request_beta = create_web_service_request('/beta/')
381 >>> alsoProvides(request_beta, IWebServiceRequestBeta)391 >>> alsoProvides(request_beta, IBetaVersion)
382 >>> beta_entry = getMultiAdapter((C1, request_beta), IEntry)392 >>> beta_entry = getMultiAdapter((C1, request_beta), IEntry)
383 >>> print beta_entry.fax393 >>> print beta_entry.fax
384 111-2121394 111-2121
385395
386 >>> request_10 = create_web_service_request('/1.0/')396 >>> request_10 = create_web_service_request('/1.0/')
387 >>> alsoProvides(request_10, IWebServiceRequest10)397 >>> alsoProvides(request_10, I10Version)
388 >>> one_oh_entry = getMultiAdapter((C1, request_10), IEntry)398 >>> one_oh_entry = getMultiAdapter((C1, request_10), IEntry)
389 >>> print one_oh_entry.fax_number399 >>> print one_oh_entry.fax_number
390 111-2121400 111-2121
391401
392 >>> request_dev = create_web_service_request('/dev/')402 >>> request_dev = create_web_service_request('/dev/')
393 >>> alsoProvides(request_dev, IWebServiceRequestDev)403 >>> alsoProvides(request_dev, IDevVersion)
394 >>> dev_entry = getMultiAdapter((C1, request_dev), IEntry)404 >>> dev_entry = getMultiAdapter((C1, request_dev), IEntry)
395 >>> print dev_entry.fax405 >>> print dev_entry.fax
396 Traceback (most recent call last):406 Traceback (most recent call last):
@@ -402,16 +412,20 @@
402412
403The set of contacts publishes a slightly different named operation in413The set of contacts publishes a slightly different named operation in
404every version of the web service, so in a little bit we'll be414every version of the web service, so in a little bit we'll be
405implementing three different versions of the same named operation. But415implementing three different versions of the same named operation. The
406the contact set itself doesn't change between versions (although it416contact set itself also changes between versions. In the 'beta' and
407could). So it's sufficient to implement one ICollection implementation417'1.0' versions, the contact set serves all contcts. In the 'dev'
408and register it as the implementation for every version of the web418version, the contact set omits contacts that only have a fax
409service.419number. We'll implement this behavior by implementing ICollection
420twice and registering each implementation for the appropriate versions
421of the web service.
410422
411 >>> from lazr.restful import Collection423 >>> from lazr.restful import Collection
412 >>> from lazr.restful.interfaces import ICollection424 >>> from lazr.restful.interfaces import ICollection
413425
414 >>> class ContactCollection(Collection):426First we'll implement the version used in 'beta' and '1.0'.
427
428 >>> class ContactCollectionBeta(Collection):
415 ... """A collection of contacts, exposed through the web service."""429 ... """A collection of contacts, exposed through the web service."""
416 ... adapts(IContactSet)430 ... adapts(IContactSet)
417 ...431 ...
@@ -425,24 +439,55 @@
425439
426 >>> from zope.interface.verify import verifyObject440 >>> from zope.interface.verify import verifyObject
427 >>> contact_set = ContactSet()441 >>> contact_set = ContactSet()
428 >>> verifyObject(ICollection, ContactCollection(contact_set, None))442 >>> verifyObject(ICollection, ContactCollectionBeta(contact_set, None))
429 True443 True
430444
431Register it as the ICollection adapter for IContactSet. We use a445Register it as the ICollection adapter for IContactSet in
432generic request interface (IWebServiceClientRequest) rather than a446IBetaVersion and I10Version.
433specific one like IWebServiceRequestBeta, so that the same
434implementation will be used for every version of the web service.
435447
436 >>> sm.registerAdapter(448 >>> for version in [IBetaVersion, I10Version]:
437 ... ContactCollection, [IContactSet, IWebServiceClientRequest],449 ... sm.registerAdapter(
438 ... provided=ICollection)450 ... ContactCollectionBeta, [IContactSet, version],
451 ... provided=ICollection)
439452
440Make sure the functionality works properly.453Make sure the functionality works properly.
441454
442 >>> collection = getMultiAdapter(455 >>> collection = getMultiAdapter(
443 ... (contact_set, request_beta), ICollection)456 ... (contact_set, request_beta), ICollection)
444 >>> len(collection.find())457 >>> len(collection.find())
445 2458 3
459
460 >>> collection = getMultiAdapter(
461 ... (contact_set, request_10), ICollection)
462 >>> len(collection.find())
463 3
464
465Now let's implement the different version used in 'dev'.
466
467 >>> class ContactCollectionDev(ContactCollectionBeta):
468 ... def find(self):
469 ... """Find all the contacts, sorted by name."""
470 ... return self.context.getContactsWithPhone()
471
472This class also implements ICollection.
473
474 >>> verifyObject(ICollection, ContactCollectionDev(contact_set, None))
475 True
476
477Register it as the ICollection adapter for IContactSet in
478IDevVersion.
479
480 >>> sm.registerAdapter(
481 ... ContactCollectionDev, [IContactSet, IDevVersion],
482 ... provided=ICollection)
483
484Make sure the functionality works properly. Note that the contact that
485only has a fax number no longer shows up.
486
487 >>> collection = getMultiAdapter(
488 ... (contact_set, request_dev), ICollection)
489 >>> [contact.name for contact in collection.find()]
490 ['Cleo Python', 'Oliver Bluth']
446491
447Implementing the named operations492Implementing the named operations
448---------------------------------493---------------------------------
@@ -479,11 +524,11 @@
479'beta' service, and the 'find' operation in the '1.0' service.524'beta' service, and the 'find' operation in the '1.0' service.
480525
481 >>> sm.registerAdapter(526 >>> sm.registerAdapter(
482 ... FindContactsOperationBase, [IContactSet, IWebServiceRequestBeta],527 ... FindContactsOperationBase, [IContactSet, IBetaVersion],
483 ... provided=IResourceGETOperation, name="findContacts")528 ... provided=IResourceGETOperation, name="findContacts")
484529
485 >>> sm.registerAdapter(530 >>> sm.registerAdapter(
486 ... FindContactsOperationBase, [IContactSet, IWebServiceRequest10],531 ... FindContactsOperationBase, [IContactSet, I10Version],
487 ... provided=IResourceGETOperation, name="find")532 ... provided=IResourceGETOperation, name="find")
488533
489Here's the slightly different named operation as implemented in534Here's the slightly different named operation as implemented in
@@ -500,7 +545,7 @@
500 ... return str(e)545 ... return str(e)
501546
502 >>> sm.registerAdapter(547 >>> sm.registerAdapter(
503 ... FindContactsOperationNoFax, [IContactSet, IWebServiceRequestDev],548 ... FindContactsOperationNoFax, [IContactSet, IDevVersion],
504 ... provided=IResourceGETOperation, name="find")549 ... provided=IResourceGETOperation, name="find")
505550
506The service root resource551The service root resource
@@ -573,14 +618,14 @@
573 ... create_web_service_request)618 ... create_web_service_request)
574619
575 >>> request_beta = create_web_service_request('/beta/')620 >>> request_beta = create_web_service_request('/beta/')
576 >>> IWebServiceRequestBeta.providedBy(request_beta)621 >>> IBetaVersion.providedBy(request_beta)
577 False622 False
578623
579The traversal process associates the request with a particular version.624The traversal process associates the request with a particular version.
580625
581 >>> request_beta.traverse(None)626 >>> request_beta.traverse(None)
582 <BetaServiceRootResource object ...>627 <BetaServiceRootResource object ...>
583 >>> IWebServiceRequestBeta.providedBy(request_beta)628 >>> IBetaVersion.providedBy(request_beta)
584 True629 True
585 >>> print request_beta.version630 >>> print request_beta.version
586 beta631 beta
@@ -620,11 +665,12 @@
620665
621 >>> body = simplejson.loads(resource())666 >>> body = simplejson.loads(resource())
622 >>> body['total_size']667 >>> body['total_size']
623 2668 3
624 >>> for link in sorted(669 >>> for link in sorted(
625 ... [contact['self_link'] for contact in body['entries']]):670 ... [contact['self_link'] for contact in body['entries']]):
626 ... print link671 ... print link
627 http://api.multiversion.dev/beta/contact_list/Cleo%20Python672 http://api.multiversion.dev/beta/contact_list/Cleo%20Python
673 http://api.multiversion.dev/beta/contact_list/Fax-your-order%20Pizza
628 http://api.multiversion.dev/beta/contact_list/Oliver%20Bluth674 http://api.multiversion.dev/beta/contact_list/Oliver%20Bluth
629675
630We can traverse through the collection to an entry.676We can traverse through the collection to an entry.
@@ -713,11 +759,12 @@
713759
714 >>> body = simplejson.loads(resource())760 >>> body = simplejson.loads(resource())
715 >>> body['total_size']761 >>> body['total_size']
716 2762 3
717 >>> for link in sorted(763 >>> for link in sorted(
718 ... [contact['self_link'] for contact in body['entries']]):764 ... [contact['self_link'] for contact in body['entries']]):
719 ... print link765 ... print link
720 http://api.multiversion.dev/1.0/contacts/Cleo%20Python766 http://api.multiversion.dev/1.0/contacts/Cleo%20Python
767 http://api.multiversion.dev/1.0/contacts/Fax-your-order%20Pizza
721 http://api.multiversion.dev/1.0/contacts/Oliver%20Bluth768 http://api.multiversion.dev/1.0/contacts/Oliver%20Bluth
722769
723We can traverse through the collection to an entry.770We can traverse through the collection to an entry.

Subscribers

People subscribed via source and target branches