[Checkins] SVN: z3c.vcsync/trunk/ Allow overwriting of objects instead of just throwing them away and

Martijn Faassen faassen at infrae.com
Tue Mar 11 14:59:25 EDT 2008


Log message for revision 84583:
  Allow overwriting of objects instead of just throwing them away and
  creating a new one.
  

Changed:
  U   z3c.vcsync/trunk/CHANGES.txt
  U   z3c.vcsync/trunk/src/z3c/vcsync/README.txt
  U   z3c.vcsync/trunk/src/z3c/vcsync/interfaces.py
  U   z3c.vcsync/trunk/src/z3c/vcsync/vc.py

-=-
Modified: z3c.vcsync/trunk/CHANGES.txt
===================================================================
--- z3c.vcsync/trunk/CHANGES.txt	2008-03-11 17:45:19 UTC (rev 84582)
+++ z3c.vcsync/trunk/CHANGES.txt	2008-03-11 18:59:25 UTC (rev 84583)
@@ -4,8 +4,16 @@
 0.12 (unreleased)
 -----------------
 
-* ...
+Features added
+~~~~~~~~~~~~~~
 
+* The developer must now also implement ``IParser`` utilities for
+  files that can be synchronized (besides ``IVcFactory``. The
+  ``IParser`` utility overwrites the existing object instead of
+  creating a new one. This allows synchronization to be a bit nicer
+  and not remove and recreate objects unnecessarily, which makes it
+  harder to implement things like references between objects.
+
 0.11 (2008-03-10)
 -----------------
 

Modified: z3c.vcsync/trunk/src/z3c/vcsync/README.txt
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/README.txt	2008-03-11 17:45:19 UTC (rev 84582)
+++ z3c.vcsync/trunk/src/z3c/vcsync/README.txt	2008-03-11 18:59:25 UTC (rev 84583)
@@ -382,27 +382,57 @@
 loading a checkout state into python objects
 --------------------------------------------
 
-Let's load the current filesystem layout into python objects. Factories
-are registered as utilities for the different things we can encounter
-on the filesystem. Let's look at items first. A factory is registered
-for the ``.test`` extension::
+Let's load the current filesystem layout into python
+objects. Factories are registered as utilities for the different
+things we can encounter on the filesystem. Let's look at items
+first. A ``IParser`` utility is registered for the ``.test``
+extension::
 
+  >>> from z3c.vcsync.interfaces import IParser
+  >>> class ItemParser(grok.GlobalUtility):
+  ...   grok.provides(IParser)
+  ...   grok.name('.test')
+  ...   def __call__(self, object, path):
+  ...      object.payload = int(path.read())
+  >>> grok.testing.grok_component('ItemParser', ItemParser)
+  True
+ 
+To have the ability to create new objects, a factory is registered for
+the ``.test`` extension as well, implemented in terms of ``ItemParser``::
+
   >>> from z3c.vcsync.interfaces import IVcFactory
+  >>> from zope import component
   >>> class ItemFactory(grok.GlobalUtility):
   ...   grok.provides(IVcFactory)
   ...   grok.name('.test')
   ...   def __call__(self, path):
-  ...       payload = int(path.read())
-  ...       return Item(payload)
+  ...       parser = component.getUtility(IParser, '.test')
+  ...       item = Item(None) # dummy payload
+  ...       parser(item, path)
+  ...       return item
   >>> grok.testing.grok_component('ItemFactory', ItemFactory)
   True
 
 Now for containers. They are registered for an empty extension::
 
+  >>> class ContainerParser(grok.GlobalUtility):
+  ...   grok.provides(IParser)
+  ...   def __call__(self, object, path):
+  ...       pass # do nothing with existing containers
+  >>> grok.testing.grok_component('ContainerParser', ContainerParser)
+  True
+
+We implement ``ContainerFactory`` in terms of
+``ContainerParser``. Note that because ``ContainerParser`` doesn't
+actually do something in this example, we could optimize it and remove
+the use of ``IParser`` here, but we won't do this for consistency::
+
   >>> class ContainerFactory(grok.GlobalUtility):
   ...   grok.provides(IVcFactory)
   ...   def __call__(self, path):
+  ...       parser = component.getUtility(IParser, '')
   ...       container = Container()
+  ...       parser(container, path)
   ...       return container
   >>> grok.testing.grok_component('ContainerFactory', ContainerFactory)
   True
@@ -655,6 +685,69 @@
   >>> container2['hoi'].payload
   2000
 
+version control changes a file into one with a different file type
+------------------------------------------------------------------
+
+Some sequence of actions by other users has ccaused a name that
+previously referred to one type of object to now refer to another kind.
+Let's define an ``Item2``::
+
+  >>> class Item2(object):
+  ...   def __init__(self, payload):
+  ...     self.payload = payload
+
+And a parser and factory for it::
+  
+  >>> class Item2Parser(grok.GlobalUtility):
+  ...   grok.provides(IParser)
+  ...   grok.name('.test2')
+  ...   def __call__(self, object, path):
+  ...      object.payload = int(path.read()) ** 2
+  >>> grok.testing.grok_component('Item2Parser', Item2Parser)
+  True 
+  >>> class Item2Factory(grok.GlobalUtility):
+  ...   grok.provides(IVcFactory)
+  ...   grok.name('.test2')
+  ...   def __call__(self, path):
+  ...       parser = component.getUtility(IParser, '.test2')
+  ...       item = Item2(None) # dummy payload
+  ...       parser(item, path)
+  ...       return item
+  >>> grok.testing.grok_component('Item2Factory', Item2Factory)
+  True
+
+Now we define an update function that replaces ``hoi.test`` with
+``hoi.test2``::
+
+  >>> hoi_path3 = root.join('hoi.test2')
+  >>> def update_function():
+  ...    hoi_path.remove()
+  ...    hoi_path3.ensure()
+  ...    hoi_path3.write('44\n')
+  >>> checkout.update_function = update_function
+  >>> checkout.up()
+
+We maintain the list of things changed::
+
+  >>> checkout._files = [hoi_path3]
+  >>> checkout._removed = [hoi_path]
+
+Reloading this will cause a new type of item to be there instead of the old
+type::
+
+  >>> s.load(None)
+  >>> isinstance(container2['hoi'], Item2)
+  True
+  >>> container2['hoi'].payload
+  1936
+
+Let's restore the original ``hoi.test`` object::
+ 
+  >>> hoi_path3.remove()
+  >>> hoi_path.write('2000\n')
+  >>> del container2['hoi']
+  >>> container2['hoi'] = Item(2000)
+
 Complete synchronization
 ------------------------
 
@@ -679,13 +772,13 @@
 The revision number before full synchronization::
 
   >>> checkout.revision_nr()
-  7
+  8
 
 Now we'll synchronize with the memory structure::
 
   >>> info = s.sync(None)
   >>> info.revision_nr
-  8
+  9
 
 We can get a report of what happened. No files were removed::
 

Modified: z3c.vcsync/trunk/src/z3c/vcsync/interfaces.py
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/interfaces.py	2008-03-11 17:45:19 UTC (rev 84582)
+++ z3c.vcsync/trunk/src/z3c/vcsync/interfaces.py	2008-03-11 18:59:25 UTC (rev 84583)
@@ -12,19 +12,42 @@
         f - an open file object to serialize this object to
         """
 
+class IParser(Interface):
+    """Load object from the filesystem into existing object.
 
+    Implement this interface as a (global) utility, registered with
+    the extension (ie .foo) it can load. The empty string extension is
+    reserved for containers.
+
+    vcsync will use this utility to overwrite objects that have been
+    changed on the filesystem.
+    """
+    def __call__(object, path):
+        """Update object with information in path.
+
+        object - the object to update with the filesystem information
+        path - the path to update from
+        """
+
 class IVcFactory(Interface):
-    """Load object from the filesystem.
+    """Load object from the filesystem into a new object.
 
-    Implement this interface for your objects (or through an adapter) to
-    let vcsync to be able to create new objects based on objects in the
-    filesystem.
+    Implement this interface as a (global) utility, registered with
+    the extension (ie .foo) it can load. The empty string extension is
+    reserved for containers.
+
+    vcsync will use this utility to create new objects based on
+    objects in the filesystem.
+
+    Typically this can be implemented in terms of IParser.
     """
     
     def __call__(path):
         """Create new instance of object.
 
         path - a py.path reference to the object to load from the filesystem
+
+        Returns the newly created object.
         """
 
 class IState(Interface):

Modified: z3c.vcsync/trunk/src/z3c/vcsync/vc.py
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/vc.py	2008-03-11 17:45:19 UTC (rev 84582)
+++ z3c.vcsync/trunk/src/z3c/vcsync/vc.py	2008-03-11 18:59:25 UTC (rev 84583)
@@ -7,7 +7,7 @@
 from zope.app.container.interfaces import IContainer
 from zope.traversing.interfaces import IPhysicallyLocatable
 
-from z3c.vcsync.interfaces import (IVcDump, ISerializer,
+from z3c.vcsync.interfaces import (IVcDump, ISerializer, IParser,
                                    IState, IVcFactory, ISynchronizer,
                                    ISynchronizationInfo)
 
@@ -197,11 +197,17 @@
             container = resolve_container(root, self.checkout.path, file_path)
             if container is None:
                 continue
-            factory = getUtility(IVcFactory, name=file_path.ext)
             name = file_path.purebasename
+            ext = file_path.ext
+
+            # if we already have the object, overwrite it, otherwise
+            # create a new one
             if name in container:
-                del container[name]
-            container[name] = factory(file_path)
+                parser = getUtility(IParser, name=ext)
+                parser(container[name], file_path)
+            else:
+                factory = getUtility(IVcFactory, name=ext)
+                container[name] = factory(file_path)
 
     def _get_container_path(self, root, obj):
         steps = []



More information about the Checkins mailing list