Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Test-driven development: a case study

1,660 views

Published on

How test-driven development enabled Gnowsis to reimplement Refinder's basic data model.

Talk at Warsaw DjangoPiwo meetup on 2011-11-02.

Published in: Technology, Education
  • Be the first to comment

  • Be the first to like this

Test-driven development: a case study

  1. 1. TDD
  2. 2. RED GREENREFACTOR
  3. 3. Effect?
  4. 4. More code
  5. 5. More simple code
  6. 6. More simple code, delivered faster
  7. 7. Cleanerinterfaces
  8. 8. Betterunderstanding of codebase
  9. 9. Fast & precise regression discovery
  10. 10. Easyrefactoring
  11. 11. Casestudy
  12. 12. Things come together. Semantic socialcollaboration platform
  13. 13. Things come together.Python/DjangoPostgreSQLApache SolrCustom Java backend
  14. 14. RDF-based APIsGraphs of linked dataURIs as identifiersSubject-Predicate-Objecttriples
  15. 15. <_:me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> .<_:me> <http://xmlns.com/foaf/0.1/family_name> "Pasternacki" .<_:me> <http://xmlns.com/foaf/0.1/givenname> "Maciej" .<_:me> <http://xmlns.com/foaf/0.1/homepage> <http://www.pasternacki.net/> .<_:me> <http://xmlns.com/foaf/0.1/mbox> <mailto:maciej@pasternacki.net> .<_:me> <http://xmlns.com/foaf/0.1/name> "Maciej Pasternacki" .<_:me> <http://xmlns.com/foaf/0.1/nick> "japhy" .<_:me> <http://xmlns.com/foaf/0.1/workplaceHomepage> <http://www.gnowsis.com/> . @prefix : <#> . @prefix foaf: <http://xmlns.com/foaf/0.1/> . :me a foaf:Person . :me foaf:family_name "Pasternacki" . :me foaf:givenname "Maciej" . :me foaf:homepage <http://www.pasternacki.net/> . :me foaf:mbox <mailto:maciej@pasternacki.net> . :me foaf:name "Maciej Pasternacki" . :me foaf:nick "japhy" . :me foaf:workplaceHomepage <http://www.gnowsis.com/> .
  16. 16. Item Relation uuid : uuid type : uri rdf_type : URI * label : string created : dateTime lastModified: dateTime Description text : string target (1) Thing Annotation Rating value : normalizedFloat InformationElement Wiki text : string Property File type : uripath : string lexicalForm : stringmimeType : string... Webpage url : string summary : string StringProperty Email subject : string value : string ... IntProperty value : integer Blogpost Tweet DatetimeProperty timestamp : dateTime author : string value : datetime author : string text : string ... ...
  17. 17. def promote(self): if not hasattr(self, _promoted): if isinstance(self, self.get_class()): self._promoted = self else: self._promoted = getattr( self, getattr( self.get_class(), self._root_class_name + _ptr ).field.related_query_name() ) return self._promotedSELECT … FROM "pim_annotation"LEFT OUTER JOIN "pim_related"ON ("pim_annotation"."id" = "pim_related"."annotation_ptr_id")WHERE ( "pim_annotation"."thing_id" = E∴ OR "pim_related"."related_to_id" = E∴ )ORDER BY "pim_annotation"."lastModified" DESC;
  18. 18. NoSQL?
  19. 19. NoSQL?
  20. 20. “How FriendFeeduses MySQL to store schema-less data”-- http://bret.appspot.com/entry/how-friendfeed-uses-mysql
  21. 21. Use PostgreSQLas RDF document-oriented database
  22. 22. Single Thing classMany concreteAnnotation classesTEXT field `attributes’for free-form RDF
  23. 23. No JOINs
  24. 24. RulesKeep existing code(when possible)Keep existing behaviourKeep magic localized
  25. 25. I. New codeRDFField & URIRefField usingrdflibCustomized RDF serialization
  26. 26. II. AnalysisExtract existing schema frommodel codeDeclare schema in RDFGlue code for attribute access toRDF data
  27. 27. nfo:Bookmark rdfs:subClassOf pimo:Thing ; grfs:className "Bookmark" ; grfs:label "nfo:Bookmark" ; grfs:pluralLabel "nfo:Bookmarks" ; grfs:isGuiCreatable "true"^^xsd:boolean ; grfs:labelProperties ( dct:title rdfs:label nfo:bookmarks ) ; grfs:shortSummaryInfoProperties ( nfo:bookmarks ) .nao:description a rdfs:Property ; rdfs:domain pimo:Thing, pimo:Person, pimo:Event, nfo:Bookmark,pimo:Project, pimo:Person, nfo:FileDataObject, pimo:Topic, pimo:Location,pimo:Task, nmo:Email, pimo:Question, pimo:Organization, pimo:Note,pimo:Collection; grfs:attributeName "description" ; grfs:ftsFullText "true"^^xsd:boolean ; grfs:additionalShortSummaryInfo "false"^^xsd:boolean .nfo:bookmarks a rdfs:Property ; rdfs:domain nfo:Bookmark ; grfs:attributeName "bookmarks" ; grfs:ftsFullText "true"^^xsd:boolean ; grfs:additionalShortSummaryInfo "true"^^xsd:boolean .
  28. 28. SELF_URI = URIRef(self:#)def __getattr__(self, attr): property_uri = attribute_uri(attr) oo = list(self.attributes.objects( self.SELF_URI, property_uri)) if not oo: raise AttributeError(attr) if len(oo) == 1: return oo[0].toPython() return set([o.toPython() for o in oo])
  29. 29. III. DetailsRun test suiteFind most common errorAdapt test or change codeRepeat for three weeks
  30. 30. IV. TweakingData migrationRefactor APIsOptimize
  31. 31. Success!
  32. 32. Things come together.http://www.getrefinder.com/

×