[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