[Checkins] SVN: z3c.vcsync/trunk/src/z3c/vcsync/ Rename svn.txt to README.txt.

Martijn Faassen faassen at infrae.com
Mon Apr 21 15:07:57 EDT 2008


Log message for revision 85570:
  Rename svn.txt to README.txt.
  

Changed:
  A   z3c.vcsync/trunk/src/z3c/vcsync/README.txt
  D   z3c.vcsync/trunk/src/z3c/vcsync/svn.txt

-=-
Copied: z3c.vcsync/trunk/src/z3c/vcsync/README.txt (from rev 85568, z3c.vcsync/trunk/src/z3c/vcsync/svn.txt)
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/README.txt	                        (rev 0)
+++ z3c.vcsync/trunk/src/z3c/vcsync/README.txt	2008-04-21 19:07:57 UTC (rev 85570)
@@ -0,0 +1,289 @@
+Version Control Synchronization
+===============================
+
+This package contains code that helps with handling synchronization of
+persistent content with a version control system. 
+
+This can be useful in software that needs to be able to work
+offline. The web application runs on a user's laptop that may be away
+from an internet connection. When connected again, the user syncs with
+a version control server, receiving updates that may have been made by
+others, and committing their own changes.
+
+Another advantage is that the version control system always contains a
+history of how content developed over time. The version-control based
+content can also be used for other purposes independent of the
+application.
+
+While this package has been written with other version control systems
+in mind, it has only been developed to work with SVN so far. Examples
+below are all given with SVN.
+
+The synchronization sequence is as follows:
+ 
+1) save persistent state (IState) to svn checkout (ICheckout) on the
+   same machine as the Zope application.
+
+2) ``svn up``. Subversion merges in changed made by others users that
+   were checked into the svn server.
+
+3) Any svn conflicts are automatically resolved.
+
+4) reload changes in svn checkout into persistent Python objects
+
+5) ``svn commit``.
+
+This is all happening in a single step. It can happen over and over
+again in a reasonably safe manner, as after the synchronization has
+concluded, the state of the persistent objects and that of the local
+SVN checkout will always be in sync.
+
+During synchronisation, the system tries to take care only to
+synchronize those objects and files that have changed. That is, in
+step 1) only applies those objects that have been modified, added or
+removed will have an effect on the checkout. In step 4) only those
+files that have been changed, added or removed on the filesystem due
+to the ``up`` action will change the persistent object state.
+
+The tree to synchronize
+-----------------------
+
+Content objects need to track the revision number after which it was
+last changed, so that later we can find all objects that have
+changed. In a real application we would typically track this in some
+content object (such as the application root), but here we will just
+track it globally::
+
+  >>> last_revision_nr = 0
+
+An item contains some payload data, and maintains the SVN revision
+after which it was changed. In a real program you would typically
+maintain the revision number of objects by using an annotation and
+listening to ``IObjectModifiedEvent``, but we will use a property
+here::
+
+  >>> class Item(object):
+  ...   def __init__(self, payload):
+  ...     self.payload = payload
+  ...   def _get_payload(self):
+  ...     return self._payload
+  ...   def _set_payload(self, value):
+  ...     self._payload = value
+  ...     self.revision_nr = last_revision_nr
+  ...   payload = property(_get_payload, _set_payload)
+
+We also have a ``Container`` class, set up before this test
+started. It is a class that implements enough of the dictionary API
+and implements the ``IContainer`` interface. A normal Zope 3 folder or
+Grok container will work. Let's now set up the tree::
+
+  >>> data = Container()
+  >>> data.__name__ = 'root'
+  >>> data['foo'] = Item(payload=1)
+  >>> data['bar'] = Item(payload=2)
+  >>> data['sub'] = Container()
+  >>> data['sub']['qux'] = Item(payload=3)
+
+As part of the synchronization procedure we need the ability to export
+persistent python objects to the version control checkout directory in
+the form of files and directories.
+ 
+Content is represented by an object that provides ``IState``. Two methods
+need to be implemented:
+
+* ``objects(revision_nr)``: any object that has been modified (or
+  added) since the synchronization for ``revision_nr``. Returning 'too
+  many' objects (objects that weren't modified) is safe, though less
+  efficient as they will then be re-exported.
+
+  Typically in your application this would be implemented by doing
+  a catalog search, so that they can be looked up quickly.
+
+* ``removed(revision_nr)``: any path that has had an object removed
+  from it since revision_nr.  It is safe to return paths that have
+  been removed and have since been replaced by a different object with
+  the same name. It is also safe to return 'too many' paths, though
+  less efficient as the objects in these paths may be re-exported
+  unnecessarily.
+
+  Typically in your application you would maintain a list of removed
+  objects by hooking into ``IObjectMovedEvent`` and
+  ``IObjectRemovedEvent`` and recording the paths of all objects that
+  were moved or removed. After an export it is safe to purge this
+  list.
+
+In this example, we will use a simpler, less efficient, implementation
+that goes through the entire tree to find changes::
+
+  >>> from zope.interface import implements
+  >>> from z3c.vcsync.interfaces import IState
+  >>> class TestState(object):
+  ...     implements(IState)
+  ...     def __init__(self, root):
+  ...         self.root = root
+  ...     def removed(self, revision_nr):
+  ...         return []
+  ...     def objects(self, revision_nr):
+  ...         for container in self._containers(revision_nr):
+  ...             for value in container.values():
+  ...                 if isinstance(value, Container):
+  ...                     continue
+  ...                 if value.revision_nr >= revision_nr:
+  ...                     yield value
+  ...     def _containers(self, revision_nr):
+  ...         return self._containers_helper(self.root)
+  ...     def _containers_helper(self, container):
+  ...         yield container
+  ...         for obj in container.values():
+  ...             if not isinstance(obj, Container):
+  ...                 continue
+  ...             for sub_container in self._containers_helper(obj):
+  ...                 yield sub_container
+
+Now that we have an implementation of ``IState`` that works for our
+state, let's create our ``state`` object::
+
+  >>> state = TestState(data)
+
+Reading from and writing to the filesystem
+------------------------------------------
+
+To integrate with the synchronization machinery, we need a way to dump
+a Python object to the filesystem (to an SVN working copy), and to
+parse it back to an object again.
+
+Let's grok this package first, as it provides some of the required
+infrastructure::
+
+  >>> import grok.testing
+  >>> grok.testing.grok('z3c.vcsync')
+  
+We need to provide a serializer for the Item class that takes an item
+and writes it to the filesystem to a file with a particular extension
+(``.test``)::
+
+  >>> import grok
+  >>> from z3c.vcsync.interfaces import ISerializer
+  >>> class ItemSerializer(grok.Adapter):
+  ...     grok.provides(ISerializer)
+  ...     grok.context(Item)
+  ...     def serialize(self, f):
+  ...         f.write(str(self.context.payload))
+  ...         f.write('\n')
+  ...     def name(self):
+  ...         return self.context.__name__ + '.test'
+
+We also need to provide a parser to load an object from the filesystem
+back into Python, overwriting the previously existing object::
+
+  >>> 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())
+
+Sometimes there is no previously existing object in the Python tree,
+and we need to add it. To do this we implement a factory (where we use
+the parser for the real work)::
+
+  >>> from z3c.vcsync.interfaces import IVcFactory
+  >>> from zope import component
+  >>> class ItemFactory(grok.GlobalUtility):
+  ...   grok.provides(IVcFactory)
+  ...   grok.name('.test')
+  ...   def __call__(self, path):
+  ...       parser = component.getUtility(IParser, '.test')
+  ...       item = Item(None) # dummy payload
+  ...       parser(item, path)
+  ...       return item
+
+Both parser and factory are registered per extension, in this case
+``.test``. This is the name of the utility.
+
+We register these components::
+
+  >>> grok.testing.grok_component('ItemSerializer', ItemSerializer)
+  True
+  >>> grok.testing.grok_component('ItemParser', ItemParser)
+  True
+  >>> grok.testing.grok_component('ItemFactory', ItemFactory)
+  True
+
+We also need a parser and factory for containers, registered for the
+empty extension (thus no special utility name). These can be very
+simple::
+
+  >>> class ContainerParser(grok.GlobalUtility):
+  ...     grok.provides(IParser)
+  ...     def __call__(self, object, path):
+  ...         pass
+
+  >>> class ContainerFactory(grok.GlobalUtility):
+  ...     grok.provides(IVcFactory)
+  ...     def __call__(self, path):
+  ...         return Container()
+
+  >>> grok.testing.grok_component('ContainerParser', ContainerParser)
+  True
+  >>> grok.testing.grok_component('ContainerFactory', ContainerFactory)
+  True
+
+Setting up the SVN repository
+-----------------------------
+
+Now we need an SVN repository to synchronize with. We create a test
+SVN repository now and create a svn path to a checkout::
+
+  >>> repo, wc = svn_repo_wc()
+
+We can now initialize the ``SvnCheckout`` object with the SVN path to
+the checkout we just created::
+
+  >>> from z3c.vcsync.svn import SvnCheckout
+  >>> checkout = SvnCheckout(wc)
+
+Constructing the synchronizer
+-----------------------------
+
+Now that we have the checkout and the state, we can set up a synchronizer::
+
+  >>> from z3c.vcsync import Synchronizer
+  >>> s = Synchronizer(checkout, state)
+
+Synchronization
+---------------
+
+We'll synchronize for the first time now::
+
+  >>> info = s.sync(last_revision_nr, "synchronize")
+
+We will now examine the SVN checkout to see whether the
+synchronization was success.
+
+We first introduce some helper functions that help us present the
+paths in a more readable form, relative to the base of the checkout::
+
+  >>> def pretty_path(path):
+  ...     return path.relto(wc)
+  >>> def pretty_paths(paths):
+  ...     return sorted([pretty_path(path) for path in paths])
+
+We see that the structure containers and items  has been translated to the
+same structure of directories and ``.test`` files on the filesystem::
+
+  >>> pretty_paths(wc.listdir())
+  ['root']
+  >>> pretty_paths(wc.join('root').listdir())
+  ['root/bar.test', 'root/foo.test', 'root/sub']
+  >>> pretty_paths(wc.join('root').join('sub').listdir())
+  ['root/sub/qux.test']
+
+The ``.test`` files have the payload data we expect::
+  
+  >>> print wc.join('root').join('foo.test').read()
+  1
+  >>> print wc.join('root').join('bar.test').read()
+  2
+  >>> print wc.join('root').join('sub').join('qux.test').read()
+  3

Deleted: z3c.vcsync/trunk/src/z3c/vcsync/svn.txt
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/svn.txt	2008-04-21 19:07:29 UTC (rev 85569)
+++ z3c.vcsync/trunk/src/z3c/vcsync/svn.txt	2008-04-21 19:07:57 UTC (rev 85570)
@@ -1,289 +0,0 @@
-Version Control Synchronization
-===============================
-
-This package contains code that helps with handling synchronization of
-persistent content with a version control system. 
-
-This can be useful in software that needs to be able to work
-offline. The web application runs on a user's laptop that may be away
-from an internet connection. When connected again, the user syncs with
-a version control server, receiving updates that may have been made by
-others, and committing their own changes.
-
-Another advantage is that the version control system always contains a
-history of how content developed over time. The version-control based
-content can also be used for other purposes independent of the
-application.
-
-While this package has been written with other version control systems
-in mind, it has only been developed to work with SVN so far. Examples
-below are all given with SVN.
-
-The synchronization sequence is as follows:
- 
-1) save persistent state (IState) to svn checkout (ICheckout) on the
-   same machine as the Zope application.
-
-2) ``svn up``. Subversion merges in changed made by others users that
-   were checked into the svn server.
-
-3) Any svn conflicts are automatically resolved.
-
-4) reload changes in svn checkout into persistent Python objects
-
-5) ``svn commit``.
-
-This is all happening in a single step. It can happen over and over
-again in a reasonably safe manner, as after the synchronization has
-concluded, the state of the persistent objects and that of the local
-SVN checkout will always be in sync.
-
-During synchronisation, the system tries to take care only to
-synchronize those objects and files that have changed. That is, in
-step 1) only applies those objects that have been modified, added or
-removed will have an effect on the checkout. In step 4) only those
-files that have been changed, added or removed on the filesystem due
-to the ``up`` action will change the persistent object state.
-
-The tree to synchronize
------------------------
-
-Content objects need to track the revision number after which it was
-last changed, so that later we can find all objects that have
-changed. In a real application we would typically track this in some
-content object (such as the application root), but here we will just
-track it globally::
-
-  >>> last_revision_nr = 0
-
-An item contains some payload data, and maintains the SVN revision
-after which it was changed. In a real program you would typically
-maintain the revision number of objects by using an annotation and
-listening to ``IObjectModifiedEvent``, but we will use a property
-here::
-
-  >>> class Item(object):
-  ...   def __init__(self, payload):
-  ...     self.payload = payload
-  ...   def _get_payload(self):
-  ...     return self._payload
-  ...   def _set_payload(self, value):
-  ...     self._payload = value
-  ...     self.revision_nr = last_revision_nr
-  ...   payload = property(_get_payload, _set_payload)
-
-We also have a ``Container`` class, set up before this test
-started. It is a class that implements enough of the dictionary API
-and implements the ``IContainer`` interface. A normal Zope 3 folder or
-Grok container will work. Let's now set up the tree::
-
-  >>> data = Container()
-  >>> data.__name__ = 'root'
-  >>> data['foo'] = Item(payload=1)
-  >>> data['bar'] = Item(payload=2)
-  >>> data['sub'] = Container()
-  >>> data['sub']['qux'] = Item(payload=3)
-
-As part of the synchronization procedure we need the ability to export
-persistent python objects to the version control checkout directory in
-the form of files and directories.
- 
-Content is represented by an object that provides ``IState``. Two methods
-need to be implemented:
-
-* ``objects(revision_nr)``: any object that has been modified (or
-  added) since the synchronization for ``revision_nr``. Returning 'too
-  many' objects (objects that weren't modified) is safe, though less
-  efficient as they will then be re-exported.
-
-  Typically in your application this would be implemented by doing
-  a catalog search, so that they can be looked up quickly.
-
-* ``removed(revision_nr)``: any path that has had an object removed
-  from it since revision_nr.  It is safe to return paths that have
-  been removed and have since been replaced by a different object with
-  the same name. It is also safe to return 'too many' paths, though
-  less efficient as the objects in these paths may be re-exported
-  unnecessarily.
-
-  Typically in your application you would maintain a list of removed
-  objects by hooking into ``IObjectMovedEvent`` and
-  ``IObjectRemovedEvent`` and recording the paths of all objects that
-  were moved or removed. After an export it is safe to purge this
-  list.
-
-In this example, we will use a simpler, less efficient, implementation
-that goes through the entire tree to find changes::
-
-  >>> from zope.interface import implements
-  >>> from z3c.vcsync.interfaces import IState
-  >>> class TestState(object):
-  ...     implements(IState)
-  ...     def __init__(self, root):
-  ...         self.root = root
-  ...     def removed(self, revision_nr):
-  ...         return []
-  ...     def objects(self, revision_nr):
-  ...         for container in self._containers(revision_nr):
-  ...             for value in container.values():
-  ...                 if isinstance(value, Container):
-  ...                     continue
-  ...                 if value.revision_nr >= revision_nr:
-  ...                     yield value
-  ...     def _containers(self, revision_nr):
-  ...         return self._containers_helper(self.root)
-  ...     def _containers_helper(self, container):
-  ...         yield container
-  ...         for obj in container.values():
-  ...             if not isinstance(obj, Container):
-  ...                 continue
-  ...             for sub_container in self._containers_helper(obj):
-  ...                 yield sub_container
-
-Now that we have an implementation of ``IState`` that works for our
-state, let's create our ``state`` object::
-
-  >>> state = TestState(data)
-
-Reading from and writing to the filesystem
-------------------------------------------
-
-To integrate with the synchronization machinery, we need a way to dump
-a Python object to the filesystem (to an SVN working copy), and to
-parse it back to an object again.
-
-Let's grok this package first, as it provides some of the required
-infrastructure::
-
-  >>> import grok.testing
-  >>> grok.testing.grok('z3c.vcsync')
-  
-We need to provide a serializer for the Item class that takes an item
-and writes it to the filesystem to a file with a particular extension
-(``.test``)::
-
-  >>> import grok
-  >>> from z3c.vcsync.interfaces import ISerializer
-  >>> class ItemSerializer(grok.Adapter):
-  ...     grok.provides(ISerializer)
-  ...     grok.context(Item)
-  ...     def serialize(self, f):
-  ...         f.write(str(self.context.payload))
-  ...         f.write('\n')
-  ...     def name(self):
-  ...         return self.context.__name__ + '.test'
-
-We also need to provide a parser to load an object from the filesystem
-back into Python, overwriting the previously existing object::
-
-  >>> 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())
-
-Sometimes there is no previously existing object in the Python tree,
-and we need to add it. To do this we implement a factory (where we use
-the parser for the real work)::
-
-  >>> from z3c.vcsync.interfaces import IVcFactory
-  >>> from zope import component
-  >>> class ItemFactory(grok.GlobalUtility):
-  ...   grok.provides(IVcFactory)
-  ...   grok.name('.test')
-  ...   def __call__(self, path):
-  ...       parser = component.getUtility(IParser, '.test')
-  ...       item = Item(None) # dummy payload
-  ...       parser(item, path)
-  ...       return item
-
-Both parser and factory are registered per extension, in this case
-``.test``. This is the name of the utility.
-
-We register these components::
-
-  >>> grok.testing.grok_component('ItemSerializer', ItemSerializer)
-  True
-  >>> grok.testing.grok_component('ItemParser', ItemParser)
-  True
-  >>> grok.testing.grok_component('ItemFactory', ItemFactory)
-  True
-
-We also need a parser and factory for containers, registered for the
-empty extension (thus no special utility name). These can be very
-simple::
-
-  >>> class ContainerParser(grok.GlobalUtility):
-  ...     grok.provides(IParser)
-  ...     def __call__(self, object, path):
-  ...         pass
-
-  >>> class ContainerFactory(grok.GlobalUtility):
-  ...     grok.provides(IVcFactory)
-  ...     def __call__(self, path):
-  ...         return Container()
-
-  >>> grok.testing.grok_component('ContainerParser', ContainerParser)
-  True
-  >>> grok.testing.grok_component('ContainerFactory', ContainerFactory)
-  True
-
-Setting up the SVN repository
------------------------------
-
-Now we need an SVN repository to synchronize with. We create a test
-SVN repository now and create a svn path to a checkout::
-
-  >>> repo, wc = svn_repo_wc()
-
-We can now initialize the ``SvnCheckout`` object with the SVN path to
-the checkout we just created::
-
-  >>> from z3c.vcsync.svn import SvnCheckout
-  >>> checkout = SvnCheckout(wc)
-
-Constructing the synchronizer
------------------------------
-
-Now that we have the checkout and the state, we can set up a synchronizer::
-
-  >>> from z3c.vcsync import Synchronizer
-  >>> s = Synchronizer(checkout, state)
-
-Synchronization
----------------
-
-We'll synchronize for the first time now::
-
-  >>> info = s.sync(last_revision_nr, "synchronize")
-
-We will now examine the SVN checkout to see whether the
-synchronization was success.
-
-We first introduce some helper functions that help us present the
-paths in a more readable form, relative to the base of the checkout::
-
-  >>> def pretty_path(path):
-  ...     return path.relto(wc)
-  >>> def pretty_paths(paths):
-  ...     return sorted([pretty_path(path) for path in paths])
-
-We see that the structure containers and items  has been translated to the
-same structure of directories and ``.test`` files on the filesystem::
-
-  >>> pretty_paths(wc.listdir())
-  ['root']
-  >>> pretty_paths(wc.join('root').listdir())
-  ['root/bar.test', 'root/foo.test', 'root/sub']
-  >>> pretty_paths(wc.join('root').join('sub').listdir())
-  ['root/sub/qux.test']
-
-The ``.test`` files have the payload data we expect::
-  
-  >>> print wc.join('root').join('foo.test').read()
-  1
-  >>> print wc.join('root').join('bar.test').read()
-  2
-  >>> print wc.join('root').join('sub').join('qux.test').read()
-  3



More information about the Checkins mailing list