Back to index

plone3  3.1.7
IArchivist.py
Go to the documentation of this file.
00001 #########################################################################
00002 # Copyright (c) 2004, 2005 Alberto Berti, Gregoire Weber. 
00003 # All Rights Reserved.
00004 # 
00005 # This file is part of CMFEditions.
00006 # 
00007 # CMFEditions is free software; you can redistribute it and/or modify
00008 # it under the terms of the GNU General Public License as published by
00009 # the Free Software Foundation; either version 2 of the License, or
00010 # (at your option) any later version.
00011 # 
00012 # CMFEditions is distributed in the hope that it will be useful,
00013 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 # GNU General Public License for more details.
00016 # 
00017 # You should have received a copy of the GNU General Public License
00018 # along with CMFEditions; if not, write to the Free Software
00019 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00020 #########################################################################
00021 """Intercepts/modifies saving/retrieving of versions to/from the repository.
00022 
00023 
00024 $Id: IArchivist.py,v 1.3 2005/02/23 00:29:02 gregweb Exp $
00025 """
00026 
00027 from Interface import Interface, Attribute
00028 
00029 
00030 class IArchivist(Interface):
00031     """The archivist knows how to handle saving and retrieving versionable 
00032        aspects.
00033     
00034     It decides which aspects to save to a histories storage and which
00035     aspects have to be overridden by the working copies ones on retrieve.
00036     
00037     As object ``obj`` may be passed a python reference to the object or
00038     any other kind of reference that allows the archivist dereferencing
00039     the object meant.
00040     """
00041 
00042     def prepare(obj, app_metadata=None, sys_metadata={}):
00043         """Prepares saving and registering of versionable aspects.
00044         
00045         The archivist decides which aspects of the objects are prepared
00046         to be saved and which not.
00047         
00048         Doesn't do any save operation, just returns information
00049         to allow recursively save the object.
00050         
00051         Returns an 'IPreparedObject' object.
00052         """
00053 
00054     def register(prepared_obj):
00055         """Register the object saving the initial state.
00056         
00057         Prior to a register the object has to prepared. Pass the
00058         return value of the 'prepare' method to 'prepared_obj'.
00059         """
00060 
00061     def save(prepared_obj, autoregister=None):
00062         """Saves versionable aspects of the objects current state.
00063         
00064         Set 'autoregister' to True if the object shall be registered
00065         automatically at the first save ever.
00066         
00067         Prior to a save the object has to be prepared. Pass the
00068         return value of the 'prepare' method to 'prepared_obj'.
00069         """
00070 
00071     def isUpToDate(obj=None, history_id=None, selector=None):
00072         """Check if the corking copy is up to date.
00073         
00074         Returns True if the working copy has changed since the last save
00075         or revert compared to the selected version. If selector is None,
00076         the comparison is done with the HEAD.
00077         
00078         The working copy is up to date if the modification date is the
00079         identical to the selected version.
00080         """
00081 
00082     def retrieve(obj=None, history_id=None, selector=None, preserve=()):
00083         """Retrieves a former state of an object.
00084         
00085         Requires either an object which is the working copy, or a history_id
00086         for an object if no history_id is provided the history_id will be 
00087         obtained from the working copy object.
00088         
00089         Returns an 'IVersionData' object.
00090         
00091         Set the selector to None if you like to retrieve the most actual
00092         version.
00093         
00094         Modifiers may overwrite some aspects of the retrieved version by
00095         the equivalent aspects of the working copy. Sometimes the 
00096         overwritten information is from interest. Attributes (and 
00097         subattributes) beeing from interest can be passed with the
00098         'preserve' argument. 
00099         E.g. preserve=('family_name', 'nick_name', 'real_name')
00100         """
00101 
00102     def getHistory(obj=None, history_id=None, preserve=()):
00103         """Return the history of an object.
00104         
00105         The history is a 'IHistory' object.
00106         
00107         Requires either an object which is the working copy, or a history_id
00108         for an object if no history_id is provided the history_id will be 
00109         obtained from the working copy object.
00110         
00111         Raises an 'ArchivistError' exception if the given object doesn't
00112         have a history.
00113         
00114         Modifiers may overwrite some aspects of the retrieved version by
00115         the equivalent aspects of the working copy. Sometimes the 
00116         overwritten information is from interest. Attributes (and 
00117         subattributes) beeing from interest can be passed with the
00118         'preserve' argument. 
00119         E.g. preserve=('family_name', 'nick_name', 'real_name')
00120         """
00121 
00122     def queryHistory(obj=None, history_id=None, preserve=(), default=[]):
00123         """Return the history of an object.
00124         
00125         Does the same as ``getHistory`` with the difference of returning
00126         the value supplied with ``default`` instead of raising an exception.
00127         """
00128 
00129 
00130 class IPurgeSupport(Interface):
00131     """Repository Purge Support
00132     
00133     Purging a version removes that version irrevocably.
00134     
00135     Adds ``purge`` method and extends the signature of the ``isUpToDate``, 
00136     ``retrieve``, ``getHistory`` and ``queryHistory`` methods.
00137     The defaults of the extended methods mimique the standard behaviour of 
00138     the original methods.
00139     
00140     With the introduction of purging two selection scheme exist for 
00141     retrieving versions. Either purged versions are taken into account 
00142     or not:
00143     
00144     - By passing ``countPurged=True`` purged versions are taken into
00145       account when accessing a version. When a purged version is accessed
00146       the storage tool decides what to do.
00147     - By passing ``countPurged=False`` purged versions are **not taken into
00148       account** when accessing a version.
00149     
00150     Example: 
00151     
00152     An object got saved ten times. Two versions got purged in earlier 
00153     calls. The history looks like this (``s`` means: depends on storage,
00154     ``e`` means: exception raised)::
00155     
00156       countPurged==True:
00157       
00158         selector:          0, 1, 2, 3, 4, 5, 6, 7, 8, 9
00159         version retrieved: 0, 1, 2, s, s, 5, 6, 7, 8, 9
00160         
00161       countPurged==False:
00162        
00163         selector:          0, 1, 2, 3, 4, 5, 6, 7, 8, 9
00164         version retrieved: 0, 1, 2, 5, 6, 7, 8, 9, e, e
00165     """
00166     
00167     def purge(obj=None, history_id=None, selector=None, metadata={}, 
00168               countPurged=True):
00169         """Purge a version of a content object.
00170         
00171         Requires either an object which is the working copy, or a history_id
00172         for an object if no history_id is provided the history_id will be 
00173         obtained from the working copy object.
00174         
00175         The comment and metadata passed may be used to store informations 
00176         about the reasons of the purging.
00177         
00178         Also counts purged versions if ``True`` is passed to ``countPurged``
00179         (see interface documentation for details).
00180         """
00181 
00182     def isUpToDate(obj=None, history_id=None, selector=None, 
00183                    countPurged=True):
00184         """Check if the corking copy is up to date.
00185         
00186         Returns True if the working copy has changed since the last save
00187         or revert compared to the selected version. If selector is None,
00188         the comparison is done with the HEAD.
00189         
00190         The working copy is up to date if the modification date is the
00191         identical to the selected version.
00192         
00193         Also counts purged versions if ``True`` is passed to ``countPurged``
00194         (see interface documentation for details).
00195         """
00196 
00197     def retrieve(obj=None, history_id=None, selector=None, preserve=(), 
00198                  countPurged=True):
00199         """Retrieve a former state of an object.
00200         
00201         Requires either an object which is the working copy, or a history_id
00202         for an object if no history_id is provided the history_id will be 
00203         obtained from the working copy object.
00204         
00205         Returns an 'IVersionData' object.
00206         
00207         Set the selector to None if you like to retrieve the most actual
00208         version.
00209         
00210         Modifiers may overwrite some aspects of the retrieved version by
00211         the equivalent aspects of the working copy. Sometimes the 
00212         overwritten information is from interest. Attributes (and 
00213         subattributes) beeing from interest can be passed with the
00214         'preserve' argument. 
00215         E.g. preserve=('family_name', 'nick_name', 'real_name')
00216         
00217         Also counts purged versions if ``True`` is passed to ``countPurged``
00218         (see interface documentation for details).
00219         """
00220 
00221     def getHistory(obj=None, history_id=None, preserve=(), countPurged=True):
00222         """Return the history of an object.
00223         
00224         The history is a 'IHistory' object.
00225         
00226         Requires either an object which is the working copy, or a history_id
00227         for an object if no history_id is provided the history_id will be 
00228         obtained from the working copy object.
00229         
00230         Raises an 'ArchivistError' exception if the given object doesn't
00231         have a history.
00232         
00233         Modifiers may overwrite some aspects of the retrieved version by
00234         the equivalent aspects of the working copy. Sometimes the 
00235         overwritten information is from interest. Attributes (and 
00236         subattributes) beeing from interest can be passed with the
00237         'preserve' argument. 
00238         E.g. preserve=('family_name', 'nick_name', 'real_name')
00239         
00240         Also counts purged versions if ``True`` is passed to ``countPurged``
00241         (see interface documentation for details).
00242         """
00243 
00244     def queryHistory(obj=None, history_id=None, preserve=(), default=[], 
00245                      countPurged=True):
00246         """Return the history of an object.
00247         
00248         Does the same as ``getHistory`` with the difference of returning
00249         the value supplied with ``default`` instead of raising an exception.
00250         
00251         Also counts purged versions if ``True`` is passed to ``countPurged``
00252         (see interface documentation for details).
00253         """
00254 
00255 
00256 class IPreparedObject(Interface):
00257     """Contains data prepared for save or register.
00258     """
00259 
00260     history_id = Attribute(
00261         """The id of the objects history.
00262         """)
00263 
00264     original = Attribute(
00265         """The unaltered original object before the modifiers were applied.
00266         
00267         This is a 'IObjectData' object.
00268         
00269         The original object shall not be modified!
00270         """)
00271     
00272     clone = Attribute(
00273         """The cloned object and version aware reference info.
00274         
00275         This is a 'IObjectData' object.
00276         """)
00277     
00278     referenced_data = Attribute(
00279         """Data that is passed to the storage by reference.
00280         
00281         These is an optimization for the case where a big blob (e.g. a Word 
00282         file) has to be saved but you want to avoid costy cloning beeing done 
00283         by the archivist.
00284         
00285         Returns a dictionary of the following format:
00286         
00287             {'name': pyref_to_object, ...}
00288         """)
00289     
00290     metadata = Attribute(
00291         """Metadata to be passed to history storage.
00292         """)
00293         
00294     is_registered = Attribute(
00295         """True if already registered by the Archivist.
00296         """)
00297 
00298 
00299 class IVersionData(Interface):
00300     """
00301     """
00302     
00303     data = Attribute(
00304         """The previously saved object.
00305         
00306         This is a 'IObjectData' object.
00307         """)
00308     
00309     refs_to_be_deleted = Attribute(
00310         """List of references to be deleted on revert.
00311         
00312         The items (containing the reference informations) are of 
00313         ``IReferenceAdapter``.
00314         """)
00315     
00316     attr_handling_references = Attribute(
00317         """List of names of attributes handling references.
00318         """)
00319         
00320     preserved_data = Attribute(
00321         """Returns data beeing preserved from beeing overwritten by modifiers.
00322         
00323         The preserved data is a flat dictionary. With the example from above:
00324         nick_name = obj.preserved_data['nick_name']
00325         """)
00326     
00327     sys_metadata = Attribute(
00328         """System related metadata.
00329         
00330         A Dictionary with the following keys:
00331         
00332         - timestamp: save time
00333         - principal: the actor that did the save
00334         - parent: Dictionary with ``history_id``, ``version_id`` and 
00335           ``location_id``
00336         """)
00337         
00338     app_metadata = Attribute(
00339         """Metadata stored alongside when the objects state was saved.
00340         """)
00341 
00342 
00343 class IHistory(Interface):
00344     """Iterable version history.
00345     """
00346 
00347     def __init__(archivist, obj):
00348         """Instantiates a lazy iterable history.
00349         
00350         This is a multi adapter adpating the archivist, the object and
00351         optionally a context wrapper.
00352         """
00353 
00354     def __len__():
00355         """Returns the length of the history.
00356         """
00357 
00358     def __getattr__(version_id):
00359         """Returns the version of an object corresponding to the version id.
00360         
00361         The object returned is of 'IVersionData'.
00362         """
00363 
00364     def __iter__():
00365         """Returns an ordered set of versions for beeing looped over.
00366         
00367         The objects returned are of 'IVersionData'.
00368         """
00369 
00370 
00371 class IObjectData(Interface):
00372     """The object including informations about outgoing references.
00373     """
00374     
00375     object = Attribute(
00376         """The object with some of the python references replaced by 
00377            version aware references.
00378         """)
00379         
00380     inside_refs = Attribute(
00381         """List of 'IAttributeAdapter' objects adapting "object inside"
00382            'IVersionAwareReference'.
00383         """)
00384 
00385     outside_refs = Attribute(
00386         """List of 'IAttributeAdapter' objects adapting "object outside"
00387            'IVersionAwareReference'.
00388         """)
00389 
00390 
00391 class IAttributeAdapter(Interface):
00392     """Adapter allowing setting and getting an attribute.
00393     
00394     TODO: use ``Attribute`` instead of explicit setters/getters.
00395     TODO: remove ``__init__`` from signature.
00396     """
00397     
00398     def __init__(parent, attr_name, type=None):
00399         """Store the attributes "coordinates".
00400         """
00401         
00402     def setAttribute(obj):
00403         """Sets the given object as attribute.
00404         """
00405         
00406     def getAttribute():
00407         """Returns the current attribute.
00408         """
00409         
00410     def getAttributeName():
00411         """Returns the attributes name.
00412         """
00413     
00414     def getType():
00415         """Returns the attributes type.
00416         """
00417 
00418 
00419 class IVersionAwareReference(Interface):
00420     """A version aware reference references an object in the repository.
00421     
00422     It is used to replace python references on save time and may be used 
00423     to rebuild those at retrieve time.
00424     """
00425 
00426     def __init__(**info):
00427         """Store some info with the reference.
00428         
00429         referencing scenarios:
00430         
00431          hid  | vid  | lid  | remarks
00432         ------+------+------+---------------------------------------------
00433          None | d.c. | d.c. | no reference stored (reference lost on save)
00434          True | None | None | reference to a non versionable resource or
00435               |      |      | or version and location information was not
00436               |      |      | known or was irrelevant at save time
00437          True | None | True | reference to a specific location of a 
00438               |      |      | resource (one of more working copies)
00439          True | True | None | impossible combination
00440          True | True | True | reference to s specific version of a 
00441               |      |      | resource at a specific location
00442         
00443         abrev.: hid = history_id, vid = version_id, lid = location_id
00444                 "True" means "True value" in the above table.
00445         
00446         Caution: The 'info' passed gets pickled. So take care not to 
00447                  store deeply nested objects!!!
00448         """
00449 
00450     def setReference(target_obj, remove_info=True):
00451         """Set a reference to the given target object.
00452         """
00453     
00454     history_id = Attribute(
00455         """The history id of the referenced resource.
00456         
00457         Histories usually contain more than one version of a resource.
00458         
00459         May be None. In this case the reference isn't set yet or the
00460         target object isn't referenceable.
00461         """)
00462         
00463     version_id = Attribute(
00464         """The version id of the referenced resource.
00465         
00466         May be None. For the interpretation see above.
00467         """)
00468         
00469     location_id = Attribute(
00470         """The location id of the working copy of the referenced resource.
00471         
00472         May be None. For the interpretation see above.
00473         """)
00474         
00475     info = Attribute(
00476         """The info stored alongside on instantiation time.
00477         
00478         May not exist.
00479         """)
00480 
00481 
00482 class ArchivistError(Exception):
00483     """Archivist exception
00484     """
00485     pass
00486 
00487 class ArchivistRetrieveError(ArchivistError):
00488     """Raised if tried to retrieve a non existent version of a resource.
00489     """
00490     pass
00491 
00492 class ArchivistRegisterError(ArchivistError):
00493     """Raised if registering the resource failed.
00494     """
00495     pass
00496     
00497 class ArchivistSaveError(ArchivistError):
00498     """Raised if saving a new version of a resource failed.
00499     """
00500     pass
00501     
00502 class ArchivistUnregisteredError(ArchivistError):
00503     """Raised if trying to save an unregistered resource.
00504     """
00505     pass