[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