[Zope-CVS] SVN: zversioning/trunk/src/versioning/ Adapted tests
Uwe Oestermeier
uwe_oestermeier at iwm-kmrc.de
Wed Oct 13 07:36:39 EDT 2004
Log message for revision 28060:
Adapted tests
Changed:
A zversioning/trunk/src/versioning/MOTIVATION.txt
D zversioning/trunk/src/versioning/README.txt
U zversioning/trunk/src/versioning/policies.py
U zversioning/trunk/src/versioning/storage.py
-=-
Copied: zversioning/trunk/src/versioning/MOTIVATION.txt (from rev 28045, zversioning/trunk/src/versioning/README.txt)
===================================================================
--- zversioning/trunk/src/versioning/README.txt 2004-10-13 08:27:38 UTC (rev 28045)
+++ zversioning/trunk/src/versioning/MOTIVATION.txt 2004-10-13 11:36:38 UTC (rev 28060)
@@ -0,0 +1,223 @@
+======================================
+Motivation for a new Versioning Scheme
+======================================
+
+
+We start by testing some of the existing infrastructure from zope.app.versioncontrol
+and try to apply the existing versioning to sample data. We take a simple
+folder tree with the following structure :
+
+ sample
+ |--> a <--|
+ |--> b |
+ |--> c
+
+
+ >>> import zope.app.versioncontrol.interfaces
+ >>> from zope.interface import directlyProvides
+ >>> from zope.app.versioncontrol.repository import declare_versioned
+ >>> from versioning.tests.repository_setup import registerAdapter
+ >>> from zope.app.folder import Folder, rootFolder
+ >>> from zope.app.tests.setup import setUpTraversal
+ >>> from zope.app.traversing.interfaces import IPhysicallyLocatable
+ >>> from ZODB.tests import util
+ >>> from zope.app.versioncontrol.interfaces import IVersioned
+ >>> registerAdapter()
+ >>> setUpTraversal()
+ >>> class TestFolder(Folder) :
+ ... zope.interface.implements(IPhysicallyLocatable)
+ ... def getPath(self) :
+ ... return ""
+
+ >>> sample = TestFolder()
+ >>> directlyProvides(sample, zope.app.traversing.interfaces.IContainmentRoot)
+ >>> a = sample["a"] = TestFolder()
+ >>> b = sample["b"] = TestFolder()
+ >>> c = b["c"] = TestFolder()
+ >>> for x in (sample, a, b, c) :
+ ... directlyProvides(x, zope.app.versioncontrol.interfaces.IVersionable)
+ >>> [x for x in sample.keys()]
+ [u'a', u'b']
+
+
+The interesting test case is the reference that uses references outside
+the hierarchical ones, which should be naturally handled in Zope3:
+
+ >>> c.refers_to = a
+ >>> a == c.refers_to
+ True
+
+In order to show some limitations of the current implementation we use a
+prebuild version control repository :
+
+ >>> from versioning.tests.repository_setup import buildRepository, buildDatabaseRoot
+ >>> db_root = buildDatabaseRoot()
+ >>> db_root["sample"] = sample
+ >>> repository = buildRepository()
+
+The current policy forces us to remove __parent__ and __name__. We'll do that
+by specializing the standard adapter that removes nothing:
+
+ >>> from zope.app.versioncontrol.nonversioned import StandardNonVersionedDataAdapter
+ >>> class NonVersionedAdapter(StandardNonVersionedDataAdapter) :
+ ... attrs = ("__name__", "__parent__") # remove __name__ and __parent from versioning
+
+
+ >>> from zope.app.tests import ztapi
+ >>> ztapi.provideAdapter(zope.app.versioncontrol.interfaces.IVersionable,
+ ... zope.app.versioncontrol.interfaces.INonVersionedData,
+ ... NonVersionedAdapter)
+ >>> zope.app.versioncontrol.interfaces.INonVersionedData(a) is not None
+ True
+
+Now we can put our example data under version control:
+
+ >>> repository.applyVersionControl(sample)
+ >>> repository.applyVersionControl(a)
+ >>> repository.applyVersionControl(b)
+ >>> repository.applyVersionControl(c)
+ >>> util.commit()
+ >>> [IVersioned.providedBy(x) for x in (sample, a, b, c)]
+ [True, True, True, True]
+ >>> [x for x in sample.keys()]
+ [u'a', u'b']
+
+
+The implementation in zope.app.versioncontrol breaks any database identity references
+because a pickle version is used that ignores all references that point
+outside the sub tree. In the example above this means, that the version of c loses
+its reference to a because c is not contained in a. (See
+ zope.app.versioncontrol.version.cloneByPickle)
+
+ >>> def accessVersion(repository, obj) :
+ ... info = repository.getVersionInfo(obj)
+ ... return repository.getVersionOfResource(info.history_id, 'mainline')
+ >>> new_a = accessVersion(repository, a)
+ >>> new_b = accessVersion(repository, b)
+ >>> new_c = accessVersion(repository, c)
+ >>> [x for x in sample.keys()]
+ [u'a', u'b']
+
+Now the reference from b to c is invalid ...
+
+ >>> new_b["c"] == new_c
+ False
+
+as well as the reference from c to a :
+
+ >>> new_c.refers_to == new_a
+ False
+
+
+This demonstrates that the reference to a is not preserved, which is the major
+motivation for a new implementation.
+
+
+
+A First Implementation
+======================
+
+We want to use versioning with objects other than standard zope objects that use
+only the standard containment structure meachanism. In the same time some parts
+of the versioning system should be pluggable, e.g. the storage for the object histories,
+the locking mechanism etc.
+
+Let's set up some initial content :
+
+ >>> a.text = "First text version of a"
+ >>> c.text = "First text version of c"
+
+To achieve this goal we refactored the components of the versioning system.
+Let's start with the basic building block, a storage that holds version histories
+of several objects. Note that this implementation does not collide with the
+implementation in zope.app.versioncontrol. This versioning scheme does not attach any
+information to the versioned objects and keeps the necessary bookeeping information
+encapsulated in the storage of object histories.
+
+
+ >>> from versioning.storage import SimpleHistoryStorage
+ >>> histories = SimpleHistoryStorage()
+
+The versioning system that puts object under versioning control must register
+the object in the histories storage (this will be called later on by
+IVersionControl.applyVersionControl) :
+
+ >>> histories.register(sample)
+ '\x00\x00\x00\x00\x00\x00\x00\x01'
+ >>> histories.register(a)
+ '\x00\x00\x00\x00\x00\x00\x00\x04'
+ >>> histories.register(b)
+ '\x00\x00\x00\x00\x00\x00\x00\x05'
+ >>> histories.register(c)
+ '\x00\x00\x00\x00\x00\x00\x00\t'
+ >>> util.commit()
+ >>> len(histories.values())
+ 4
+
+The SimpleHistoryStorage.register method copies nothing. The copy process is
+managed by a multi adapter that implements a versioning policy, i.e. determines
+how and what is versioned. The implementation here uses zope.app.copypastemove
+to save each version as a normal copy of the original content object.
+
+ >>> from versioning.policies import VersionableAspectsAdapter
+ >>> def saveAsVersion(obj, histories) : # a helper method XXX remove later
+ ... adapter = VersionableAspectsAdapter(obj, histories)
+ ... return adapter.writeAspects()
+ >>> saveAsVersion(sample, histories)
+ '001'
+ >>> saveAsVersion(a, histories)
+ '001'
+ >>> saveAsVersion(b, histories)
+ '001'
+ >>> saveAsVersion(c, histories)
+ '001'
+ >>> version = histories[a._p_oid]['001']
+ >>> version.text
+ 'First text version of a'
+
+Add some additional content versions :
+
+ >>> a.text = "Second text version of a"
+ >>> c.text = "Second text version of c"
+ >>> saveAsVersion(a, histories)
+ '002'
+ >>> version = histories[a._p_oid]['002']
+ >>> version.text
+ 'Second text version of a'
+ >>> saveAsVersion(c, histories)
+ '002'
+
+Now let's update the original object with versioned data. This again is a matter
+of the policy. The simple implementation here replaces the original data
+with a copy of the versioned data.
+
+ >>> def revertToVersion(obj, histories, selector) :
+ ... adapter = VersionableAspectsAdapter(obj, histories)
+ ... adapter.updateAspects(selector)
+ >>> revertToVersion(c, histories, '001')
+
+The fact that we used copies here requires
+that we use new references that are established via traversal:
+
+ >>> new_c = b["c"]
+ >>> new_c.text
+ 'First text version of c'
+
+When we get back a version the reference to an old and untouced items is still
+intact :
+
+ >>> new_c.refers_to == a
+ True
+
+The more interesting case arises if we update the refered object too :
+
+ >>> revertToVersion(a, histories, '001')
+ >>> new_a = sample["a"]
+ >>> new_c.refers_to == a
+ True
+ >>> new_c.refers_to == new_a
+ False
+
+Depending on the use case this might be what you want.
+
+
Deleted: zversioning/trunk/src/versioning/README.txt
===================================================================
--- zversioning/trunk/src/versioning/README.txt 2004-10-13 11:05:24 UTC (rev 28059)
+++ zversioning/trunk/src/versioning/README.txt 2004-10-13 11:36:38 UTC (rev 28060)
@@ -1,152 +0,0 @@
-Versioning
-==========
-
-
-We start by testing some of the existing infrastructure from zope.app.versioncontrol
-and try to apply the existing versioning to sample data. We take a simple
-folder tree with the following structure :
-
- sample
- |--> a <--|
- |--> b |
- |--> c
-
-
- >>> import zope.app.versioncontrol.interfaces
- >>> from zope.interface import directlyProvides
- >>> from zope.app.versioncontrol.repository import declare_versioned
- >>> from versioning.tests.repository_setup import registerAdapter
- >>> from zope.app.folder import Folder, rootFolder
- >>> from zope.app.tests.setup import setUpTraversal
- >>> from zope.app.traversing.interfaces import IPhysicallyLocatable
- >>> from ZODB.tests import util
- >>> from zope.app.versioncontrol.interfaces import IVersioned
- >>> registerAdapter()
- >>> setUpTraversal()
- >>> class TestFolder(Folder) :
- ... zope.interface.implements(IPhysicallyLocatable)
- ... def getPath(self) :
- ... return ""
-
- >>> sample = TestFolder()
- >>> directlyProvides(sample, zope.app.traversing.interfaces.IContainmentRoot)
- >>> a = sample["a"] = TestFolder()
- >>> b = sample["b"] = TestFolder()
- >>> c = b["c"] = TestFolder()
- >>> for x in (sample, a, b, c) :
- ... directlyProvides(x, zope.app.versioncontrol.interfaces.IVersionable)
-
-The interesting test case is the reference that uses references outside
-the hierarchical ones, which should be naturally handled in Zope3:
-
- >>> c.refers_to = a
- >>> a == c.refers_to
- True
-
-In order to show some limitations of the current implementation we use a
-prebuild version control repository :
-
- >>> from versioning.tests.repository_setup import buildRepository, buildDatabaseRoot
- >>> db_root = buildDatabaseRoot()
- >>> db_root["sample"] = sample
- >>> repository = buildRepository()
-
-The current policy forces us to remove __parent__ and __name__. We'll do that
-by specializing the standard adapter that removes nothing:
-
- >>> from zope.app.versioncontrol.nonversioned import StandardNonVersionedDataAdapter
- >>> class NonVersionedAdapter(StandardNonVersionedDataAdapter) :
- ... attrs = ("__name__", "__parent__") # remove __name__ and __parent from versioning
-
-
- >>> from zope.app.tests import ztapi
- >>> ztapi.provideAdapter(zope.app.versioncontrol.interfaces.IVersionable,
- ... zope.app.versioncontrol.interfaces.INonVersionedData,
- ... NonVersionedAdapter)
- >>> zope.app.versioncontrol.interfaces.INonVersionedData(a) is not None
- True
-
-Now we can put our example data under version control:
-
- >>> repository.applyVersionControl(sample)
- >>> repository.applyVersionControl(a)
- >>> repository.applyVersionControl(b)
- >>> repository.applyVersionControl(c)
- >>> util.commit()
- >>> [IVersioned.providedBy(x) for x in (sample, a, b, c)]
- [True, True, True, True]
-
-
-The implementation in zope.app.versioncontrol breaks any database identity references
-because a pickle version is used that ignores all references that point
-outside the sub tree. In the example above this means, that the version of c loses
-its reference to a because c is not contained in a. (See
- zope.app.versioncontrol.version.cloneByPickle)
-
- >>> def accessVersion(repository, obj) :
- ... info = repository.getVersionInfo(obj)
- ... return repository.getVersionOfResource(info.history_id, 'mainline')
- >>> new_a = accessVersion(repository, a)
- >>> new_b = accessVersion(repository, b)
- >>> new_c = accessVersion(repository, c)
-
-Now the reference from b to c is invalid ...
-
- >>> new_b["c"] == new_c
- False
-
-as well as the reference from c to a :
-
- >>> new_c.refers_to == new_a
- False
-
-
-This demonstrates that the reference to a is not preserved, which is the major
-motivation for a new implementation.
-
-
-
-
-Alternative implementation
---------------------------
-
-We want to use versioning with objects other than standard zope objects that use
-only the standard containment structure meachanism. In the same time some parts
-of the versioning system should be pluggable, e.g. the storage for the object histories,
-the locking mechanism etc.
-
-We start with the basic building blocks, a storage that holds version histories
-of several objects. Note that this implementation does not collide with the
-implementation in zope.app.versioncontrol. This versioning scheme does not attach any
-information to the versioned objects and keeps the necessary bookeeping information
-encapsulated in the storage of object histories.
-
- >>> from versioning.storage import SimpleHistoryStorage
- >>> from versioning.policies import VersionableAspectsAdapter
- >>> histories = SimpleHistoryStorage()
- >>> histories.register(a)
- '\x00\x00\x00\x00\x00\x00\x00\x04'
- >>> histories.register(b)
- '\x00\x00\x00\x00\x00\x00\x00\x05'
- >>> util.commit()
- >>> len(histories.values())
- 2
- >>> [x for x in histories.keys()]
- [u'\x00\x00\x00\x00\x00\x00\x00\x04', u'\x00\x00\x00\x00\x00\x00\x00\x05']
- >>> adapter = VersionableAspectsAdapter(a, histories)
- >>> adapter.writeAspects()
- '001'
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Modified: zversioning/trunk/src/versioning/policies.py
===================================================================
--- zversioning/trunk/src/versioning/policies.py 2004-10-13 11:05:24 UTC (rev 28059)
+++ zversioning/trunk/src/versioning/policies.py 2004-10-13 11:36:38 UTC (rev 28060)
@@ -76,8 +76,9 @@
history = self.histories.getHistory(self.versionable)
version = history[version_specifier]
parent = self.versionable.__parent__
- del parent[self.versionable.__name__]
- IObjectCopier(self.versionable).copyTo(parent)
+ name = self.versionable.__name__
+ del parent[name]
+ IObjectCopier(version).copyTo(parent, name)
Modified: zversioning/trunk/src/versioning/storage.py
===================================================================
--- zversioning/trunk/src/versioning/storage.py 2004-10-13 11:05:24 UTC (rev 28059)
+++ zversioning/trunk/src/versioning/storage.py 2004-10-13 11:36:38 UTC (rev 28060)
@@ -89,6 +89,11 @@
ticket = self.getTicket(obj)
return self[ticket]
+ def getVersion(self, obj, selector) :
+ """ Returns the version of an object that is specified by selector. """
+ history = self.getHistory(obj)
+ return history[selector]
+
More information about the Zope-CVS
mailing list