[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/versioncontrol/ Reflected checked in/out status in interfaces so that we can dispatch

Jim Fulton jim at zope.com
Thu Jul 28 12:34:53 EDT 2005


Log message for revision 37533:
  Reflected checked in/out status in interfaces so that we can dispatch
  to components based on this status.
  
  Added a repository copyVersion method for copying data from a version
  to a checked out object.
  

Changed:
  U   Zope3/trunk/src/zope/app/versioncontrol/README.txt
  U   Zope3/trunk/src/zope/app/versioncontrol/interfaces.py
  U   Zope3/trunk/src/zope/app/versioncontrol/repository.py

-=-
Modified: Zope3/trunk/src/zope/app/versioncontrol/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/versioncontrol/README.txt	2005-07-28 16:26:49 UTC (rev 37532)
+++ Zope3/trunk/src/zope/app/versioncontrol/README.txt	2005-07-28 16:34:53 UTC (rev 37533)
@@ -13,12 +13,13 @@
 
 It also requires that instances support `IPersistent` and `IAnnotatable`.
 
-Normally, these interfaces will be provided by adapters.  To simplify the
-example, we'll just create a class that already implements the required
-interfaces directly.  We need to be careful to avoid including the `__name__`
-and `__parent__` attributes in state copies, so even a fairly simple
-implementation of INonVersionedData has to deal with these for objects that
-contain their own location information.
+Normally, the INonVersionedData and IPhysicallyLocatable interfaces
+will be provided by adapters.  To simplify the example, we'll just
+create a class that already implements the required interfaces
+directly.  We need to be careful to avoid including the `__name__` and
+`__parent__` attributes in state copies, so even a fairly simple
+implementation of INonVersionedData has to deal with these for objects
+that contain their own location information.
 
     >>> import persistent
     >>> from zope import component, interface
@@ -207,21 +208,48 @@
 applications, this parallels form-based changes to objects, but this
 is a matter of policy.
 
+When version control is applied to an object, or when an object is
+retrieved from the repository, it is checked in.  It provides
+`ICheckedIn`:
+
+    >>> interfaces.ICheckedIn.providedBy(samp)
+    True
+    >>> interfaces.ICheckedIn.providedBy(ob)
+    True
+
+It is not checked out:
+
+    >>> interfaces.ICheckedOut.providedBy(samp)
+    False
+    >>> interfaces.ICheckedOut.providedBy(ob)
+    False
+
 Let's save some information about the current version of the object so
 we can see that it changes:
 
     >>> orig_history_id = info.history_id
     >>> orig_version_id = info.version_id
 
-Now, let's check out the object and add an attribute:
+Now, let's check out the object:
 
     >>> repository.checkoutResource(ob)
     checked out <Sample object>, version 1
+
+At this point, the object provides `ICheckedOut` and not `ICheckedIn`:
+
+    >>> interfaces.ICheckedOut.providedBy(ob)
+    True
+    >>> interfaces.ICheckedIn.providedBy(ob)
+    False
+
+Now, we'll and add an attribute:
+
     >>> ob.value = 42
     >>> repository.checkinResource(ob)
     checked in <Sample object>, version 2
     >>> transaction.commit()
 
+
 We can now compare information about the updated version with the
 original information:
 
@@ -277,6 +305,14 @@
     updated <Sample object> from version 1 to 2
     >>> repository.checkoutResource(samp)
     checked out <Sample object>, version 2
+
+
+    >>> interfaces.ICheckedOut.providedBy(samp)
+    True
+    >>> interfaces.ICheckedIn.providedBy(samp)
+    False
+
+
     >>> transaction.commit()
 
     >>> repository.isResourceChanged(samp)
@@ -297,6 +333,13 @@
 
     >>> repository.checkinResource(samp, 'sample checkin')
     checked in <Sample object>, version 3
+
+
+    >>> interfaces.ICheckedIn.providedBy(samp)
+    True
+    >>> interfaces.ICheckedOut.providedBy(samp)
+    False
+
     >>> transaction.commit()
 
     >>> repository.checkoutResource(samp)
@@ -799,3 +842,42 @@
 any information about how it communicates with itself (it could store
 all the relevant data into an external file and use the value returned
 to locate the state file again, if that was needed for some reason).
+
+Copying old version data
+------------------------
+
+Sometimes, you'd like to copy old version data.  You can do so with
+`copyVersion`:
+
+    >>> ob = Sample()
+    >>> ob.__name__ = 'samp'
+    >>> root["samp"] = ob
+    >>> transaction.commit()
+    >>> ob.x = 1
+    >>> repository.applyVersionControl(ob)
+    applied version control to <Sample object>
+    >>> repository.checkoutResource(ob)
+    checked out <Sample object>, version 1
+    >>> ob.x = 2
+    >>> repository.checkinResource(ob)
+    checked in <Sample object>, version 2
+    >>> repository.copyVersion(ob, '1')
+    Traceback (most recent call last):
+    ...
+    VersionControlError: The selected resource is not checked out.
+
+    >>> repository.checkoutResource(ob)
+    checked out <Sample object>, version 2
+    >>> ob.x = 3
+    >>> transaction.commit()
+    >>> repository.copyVersion(ob, '1')
+    >>> ob.x
+    1
+
+    >>> transaction.commit()
+    >>> repository.isResourceChanged(ob)
+    True
+    >>> repository.checkinResource(ob)
+    checked in <Sample object>, version 3
+
+

Modified: Zope3/trunk/src/zope/app/versioncontrol/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/versioncontrol/interfaces.py	2005-07-28 16:26:49 UTC (rev 37532)
+++ Zope3/trunk/src/zope/app/versioncontrol/interfaces.py	2005-07-28 16:34:53 UTC (rev 37533)
@@ -124,6 +124,13 @@
         history.
         """
 
+    def copyVersion(object, selector):
+        """Copy data from an old version to a checked out object
+
+        The object's data are updated and it remains checked out and
+        modified.
+        """
+
     def labelResource(object, label, force=None):
         """
         Associate the given resource with a label. If force is true, then
@@ -301,7 +308,22 @@
 class IVersioned(IVersionable):
     """Version control is in effect for this object."""
 
+class ICheckedIn(IVersioned):
+    """Object that has been checked in.
 
+    Changes should not be allowed.
+
+    An object may be ICheckedIn or ICheckedOut, but not both,
+    """
+
+class ICheckedOut(IVersioned):
+    """Object that has been checked out.
+
+    Changes should be allowed.
+
+    An object may be ICheckedIn or ICheckedOut, but not both,
+    """
+
 # Events that are raised for interesting occurances:
 
 class IVersionEvent(zope.app.event.interfaces.IObjectEvent):

Modified: Zope3/trunk/src/zope/app/versioncontrol/repository.py
===================================================================
--- Zope3/trunk/src/zope/app/versioncontrol/repository.py	2005-07-28 16:26:49 UTC (rev 37532)
+++ Zope3/trunk/src/zope/app/versioncontrol/repository.py	2005-07-28 16:34:53 UTC (rev 37533)
@@ -35,6 +35,7 @@
 from zope.app.versioncontrol.history import VersionHistory
 from zope.app.versioncontrol.interfaces import VersionControlError
 from zope.app.versioncontrol.interfaces import IVersionable, IVersioned
+from zope.app.versioncontrol.interfaces import ICheckedIn, ICheckedOut
 from zope.app.versioncontrol.interfaces import IRepository
 from zope.app.versioncontrol.interfaces import CHECKED_IN, CHECKED_OUT
 from zope.app.versioncontrol.interfaces import ACTION_CHECKIN, ACTION_CHECKOUT
@@ -183,7 +184,7 @@
         version_id = version.__name__
 
         # Add bookkeeping information to the version controlled object.
-        declare_versioned(object)
+        declare_checked_in(object)
         info = utility.VersionInfo(history_id, version_id, CHECKED_IN)
         annotations = IAnnotations(object)
         annotations[VERSION_INFO_KEY] = info
@@ -230,6 +231,7 @@
         # Update bookkeeping information.
         info.status = CHECKED_OUT
         info.touch()
+        declare_checked_out(object)
 
         zope.event.notify(event.VersionCheckedOut(object, info))
 
@@ -272,6 +274,7 @@
         info.version_id = version.__name__
         info.status = CHECKED_IN
         info.touch()
+        declare_checked_in(object)
 
         zope.event.notify(event.VersionCheckedIn(object, info, message))
 
@@ -301,6 +304,7 @@
                                    CHECKED_IN)
         annotations = IAnnotations(object)
         annotations[VERSION_INFO_KEY] = info
+        declare_checked_in(object)
 
         zope.event.notify(event.VersionReverted(object, info))
 
@@ -370,7 +374,7 @@
         version_id = version and version.__name__ or info.version_id
         if version and (version_id != info.version_id):
             self.replaceState(object, version.copyState())
-            declare_versioned(object)
+            declare_checked_in(object)
 
             history.addLogEntry(version_id,
                                 ACTION_UPDATE,
@@ -386,6 +390,32 @@
 
         zope.event.notify(event.VersionUpdated(object, info, oldversion))
 
+    def copyVersion(self, object, selector):
+        info = self.getVersionInfo(object)
+        if info.status != CHECKED_OUT:
+            raise VersionControlError(
+                'The selected resource is not checked out.'
+                )
+
+        history = self.getVersionHistory(info.history_id)
+
+        if history.hasVersionId(selector):
+            version = history.getVersionById(selector)
+
+        elif self._labels.has_key(selector):
+            version = history.getVersionByLabel(selector)
+
+        elif self._branches.has_key(selector):
+            version = history.getLatestVersion(selector)
+        else:
+            raise VersionControlError(
+                'Invalid version selector: %s' % selector
+                    )
+        
+        self.replaceState(object, version.copyState())
+        IAnnotations(object)[VERSION_INFO_KEY] = info
+        declare_checked_out(object)
+
     def labelResource(self, object, label, force=0):
         info = self.getVersionInfo(object)
         if info.status != CHECKED_IN:
@@ -480,7 +510,7 @@
                     version = history.getVersionByDate('mainline', timestamp)
 
         object = version.copyState()
-        declare_versioned(object)
+        declare_checked_in(object)
 
         info = utility.VersionInfo(history_id, version.__name__, CHECKED_IN)
         if sticky is not None:
@@ -508,8 +538,20 @@
         return history.getLogEntries()
 
 
-def declare_versioned(object):
-    """Apply bookkeeping needed to recognize an object version controlled."""
+def declare_checked_in(object):
+    """Apply bookkeeping needed to recognize an object version controlled.
+    """
     ifaces = zope.interface.directlyProvidedBy(object)
-    ifaces += IVersioned
+    if ICheckedOut in ifaces:
+        ifaces -= ICheckedOut
+    ifaces += ICheckedIn
     zope.interface.directlyProvides(object, *ifaces)
+
+def declare_checked_out(object):
+    """Apply bookkeeping needed to recognize an object version controlled.
+    """
+    ifaces = zope.interface.directlyProvidedBy(object)
+    if ICheckedIn in ifaces:
+        ifaces -= ICheckedIn
+    ifaces += ICheckedOut
+    zope.interface.directlyProvides(object, *ifaces)



More information about the Zope3-Checkins mailing list