[Zope-CVS] CVS: Products/DBTab - CHANGES.txt:1.8 DBTab.py:1.10 Mount.py:1.6 MountedObject.py:1.5 dbtab.conf.in:1.7

Shane Hathaway shane@zope.com
Wed, 16 Apr 2003 15:59:25 -0400


Update of /cvs-repository/Products/DBTab
In directory cvs.zope.org:/tmp/cvs-serv19209

Modified Files:
	CHANGES.txt DBTab.py Mount.py MountedObject.py dbtab.conf.in 
Log Message:
You can now specify a container_factory to generate folderish
objects other than standard folders when mounting a new database.
See dbtab.conf.in.

To make this work, the mounted database has to use the product
registry from the root database when creating the mounted folders.
The new Trailblazer class encapsulates this detail by overriding the
_getProducts() method of a ProductDispatcher, simulating
OFS.ObjectManager.manage_addProduct.  This hack is actually somewhat
clean.


=== Products/DBTab/CHANGES.txt 1.7 => 1.8 ===
--- Products/DBTab/CHANGES.txt:1.7	Sat Mar 15 20:54:04 2003
+++ Products/DBTab/CHANGES.txt	Wed Apr 16 15:59:24 2003
@@ -1,11 +1,14 @@
 
-Next release (1.1.1?)
+Version 1.2
 
   - Fixed activity monitoring for mounted databases.
 
   - Removed import of AdaptableStorage.  Argument converters now work
     when you specify the full module of a storage class.
 
+  - You can now specify a container_factory to generate folderish
+    objects other than standard folders when mounting a new database.
+    See dbtab.conf.in.
 
 
 Version 1.1


=== Products/DBTab/DBTab.py 1.9 => 1.10 ===
--- Products/DBTab/DBTab.py:1.9	Sat Mar 15 20:29:25 2003
+++ Products/DBTab/DBTab.py	Wed Apr 16 15:59:24 2003
@@ -50,8 +50,8 @@
     connection_class = None
 
     # Mount options
-    mount_paths = None    # ((virtual_path, real_root, real_path),)
-    auto_create = None
+    mount_paths = None        # ((virtual_path, real_root, real_path),)
+    container_factory = None  # string: "product_name:factory_name"
 
     # Other options
     class_factory = None
@@ -174,7 +174,7 @@
 
         # Read string parameters
         for key in (
-            'class_factory',
+            'class_factory', 'container_factory',
             ):
             if args.has_key(key):
                 setattr(self, key, args[key])
@@ -184,7 +184,7 @@
 
         # Read boolean parameters
         for key in (
-            'open_at_startup', 'auto_create',
+            'open_at_startup',
             ):
             if args.has_key(key):
                 v = asBoolean(args[key])
@@ -193,6 +193,13 @@
             else:
                 setattr(self, key, None)
 
+        # Read deprecated parameters
+        for key in (
+            'auto_create',
+            ):
+            if args.has_key(key):
+                del args[key]
+
         if args.has_key('mount_paths'):
             mps = args['mount_paths']
             self.mount_paths = self.computeMountPaths(mps)
@@ -228,13 +235,13 @@
         return self.open_at_startup
 
     def getMountParams(self, mount_path):
-        """Returns (real_root, real_path, auto_create) for a virtual
+        """Returns (real_root, real_path, container_factory) for a virtual
         mount path.
         """
         if self.mount_paths:
             for (virtual_path, real_root, real_path) in self.mount_paths:
                 if virtual_path == mount_path:
-                    return (real_root, real_path, self.auto_create)
+                    return (real_root, real_path, self.container_factory)
         raise LookupError('Nothing known about mount path %s' % mount_path)
 
 


=== Products/DBTab/Mount.py 1.5 => 1.6 ===
--- Products/DBTab/Mount.py:1.5	Sat Mar 15 20:54:04 2003
+++ Products/DBTab/Mount.py	Wed Apr 16 15:59:24 2003
@@ -50,7 +50,7 @@
         """
         raise NotImplementedError
 
-    def _traverseToMountedRoot(self, root):
+    def _traverseToMountedRoot(self, root, mount_parent):
         """Hook for getting the object to be mounted.
         """
         raise NotImplementedError
@@ -85,7 +85,7 @@
                     anyjar = parent._p_jar
                 conn = self._getMountedConnection(anyjar)
                 root = conn.root()
-                obj = self._traverseToMountedRoot(root)
+                obj = self._traverseToMountedRoot(root, parent)
                 data = aq_base(obj)
                 # Store the data object in a tuple to hide from acquisition.
                 self._v_data = (data,)


=== Products/DBTab/MountedObject.py 1.4 => 1.5 ===
--- Products/DBTab/MountedObject.py:1.4	Thu Feb  6 15:30:18 2003
+++ Products/DBTab/MountedObject.py	Wed Apr 16 15:59:24 2003
@@ -22,7 +22,8 @@
 from Acquisition import aq_base, aq_inner, aq_parent
 from AccessControl.ZopeGuards import guarded_getattr
 from OFS.SimpleItem import SimpleItem
-from OFS.Folder import Folder, manage_addFolder
+from OFS.Folder import Folder
+from App.FactoryDispatcher import ProductDispatcher
 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
 
 from Mount import MountPoint
@@ -40,37 +41,55 @@
     return configuration
 
 
-def traverseOrConstruct(app, path, omit_final=0, restricted=1):
-    """Traverses a path, constructing it if necessary.
-    """
-    container = app
-    parts = filter(None, path.split('/'))
-    if omit_final:
-        if len(parts) < 1:
-            raise ValueError, 'Path %s is not a valid mount path' % path
-        parts = parts[:-1]
-    for part in parts:
-        try:
-            if restricted:
-                o = container.restrictedTraverse(part)
-            else:
-                o = container.unrestrictedTraverse(part)
-        except (KeyError, AttributeError):
-            # Try to create a Folder in this place.
-            if restricted:
-                # Check security in the process.
-                dispatcher = container.manage_addProduct['OFSP']
-                factory = guarded_getattr(dispatcher, 'manage_addFolder')
-                factory(part)
-                o = container.restrictedTraverse(part)
-            else:
-                manage_addFolder(container, part)
-                o = container.unrestrictedTraverse(part)
-            # Commit a subtransaction to assign the new object to
-            # the correct database.
-            get_transaction().commit(1)
-        container = o
-    return container
+class Trailblazer:
+    """Follows Zope paths.  If a path is not found, creates it."""
+
+    def __init__(self, base, zope_app=None, container_factory=None):
+        self.base = base
+        if zope_app is None:
+            zope_app = base
+        self.zope_app = zope_app
+        if not container_factory:
+            container_factory = 'OFSP:manage_addFolder'
+        self.product_name, self.factory_name = container_factory.split(':', 1)
+
+    def _construct(self, context, restricted, id):
+        """Returns the FactoryDispatcher for context."""
+        pd = ProductDispatcher().__of__(context)
+        # Make the installed products available to the product dispatcher
+        pd._getProducts = self.zope_app._getProducts
+        dispatcher = pd[self.product_name]
+        if restricted:
+            factory = guarded_getattr(dispatcher, self.factory_name)
+            factory(id)
+            o = context.restrictedTraverse(id)
+        else:
+            factory = getattr(dispatcher, self.factory_name)
+            factory(id)
+            o = context.unrestrictedTraverse(id)
+        # Commit a subtransaction to assign the new object to
+        # the correct database.
+        get_transaction().commit(1)
+        return o
+
+    def traverseOrConstruct(self, path, omit_final=0, restricted=1):
+        """Traverses a path, constructing it if necessary."""
+        container = self.base
+        parts = filter(None, path.split('/'))
+        if omit_final:
+            if len(parts) < 1:
+                raise ValueError, 'Path %s is not a valid mount path' % path
+            parts = parts[:-1]
+        for part in parts:
+            try:
+                if restricted:
+                    container = container.restrictedTraverse(part)
+                else:
+                    container = container.unrestrictedTraverse(part)
+            except (KeyError, AttributeError):
+                # Try to create a container in this place.
+                container = self._construct(container, restricted, part)
+        return container
 
 
 class MountedObject(MountPoint, SimpleItem):
@@ -117,19 +136,19 @@
         self._v_mount_params = params
         return params
 
-    def _traverseToMountedRoot(self, root):
+    def _traverseToMountedRoot(self, root, mount_parent):
         """Hook for getting the object to be mounted.
         """
         params = self._v_mount_params
         if params is None:
             params = self._loadMountParams()
-        real_root, real_path, auto_create = params
+        real_root, real_path, container_factory = params
         if real_root is None:
             real_root = 'Application'
         try:
             obj = root[real_root]
         except KeyError:
-            if auto_create or self._create_mount_points:
+            if container_factory or self._create_mount_points:
                 # Create a database automatically.
                 from OFS.Application import Application
                 obj = Application()
@@ -143,9 +162,10 @@
             try:
                 obj = obj.unrestrictedTraverse(real_path)
             except (KeyError, AttributeError):
-                if auto_create or self._create_mount_points:
-                    # Create the real object automatically.
-                    obj = traverseOrConstruct(obj, real_path, restricted=0)
+                if container_factory or self._create_mount_points:
+                    zopeapp = mount_parent.getPhysicalRoot()
+                    blazer = Trailblazer(obj, zopeapp, container_factory)
+                    obj = blazer.traverseOrConstruct(real_path, restricted=0)
                 else:
                     raise
         return obj
@@ -193,7 +213,8 @@
         elif getattr(o, '_isMountedObject', 0):
             # Oops, didn't actually mount!
             exists = 1
-            status = o._v_connect_error
+            t, v = o._v_connect_error[:2]
+            status = '%s: %s' % (t, v)
         else:
             exists = 1
             mp = getMountPoint(o)
@@ -231,7 +252,8 @@
         mo._create_mount_points = not not create_mount_points
         # Raise an error now if there is any problem.
         mo._test(app)
-        container = traverseOrConstruct(app, path, omit_final=1)
+        blazer = Trailblazer(app)
+        container = blazer.traverseOrConstruct(path, omit_final=1)
         mo._p_jar = container._p_jar
         loaded = mo.__of__(container)
 


=== Products/DBTab/dbtab.conf.in 1.6 => 1.7 ===
--- Products/DBTab/dbtab.conf.in:1.6	Thu Feb  6 16:17:52 2003
+++ Products/DBTab/dbtab.conf.in	Wed Apr 16 15:59:24 2003
@@ -131,7 +131,7 @@
 # [Database: Sessions]
 # connection_class=LowConflictConnection
 # mount_paths=/temp_folder
-# auto_create=1
+# container_factory=OFSP:manage_addFolder
 
 
 ######################################################################
@@ -231,34 +231,47 @@
 
 ## This loads the OFS.Application object at the root of the mounted
 ## database and makes it available as /News/Archives.  But when an
-## Application object exists anywhere besides the root of your Zope
-## site, your site is likely to break.  If you're familiar with ZODB,
-## you can programmatically arrange for the root object of the mounted
-## database to be some other kind of object.  But normally you just
-## don't need to.
+## Application object is mounted anywhere besides the root of your
+## Zope site, your site is likely to break.  If you're familiar with
+## ZODB, you can programmatically arrange for the root object of the
+## mounted database to be some other kind of object.  But normally you
+## just don't need to.
 
 ## On the other hand, if you're writing an external ZODB application
 ## and you want to mount your application into Zope, you may not have
 ## an Application at the root of your mounted ZODB at all.  The true
-## root of a ZODB is a PersistentMapping where Zope puts two keys,
-## "Application" and "ZGlobals".  If the important part of your ZODB
-## is stored under a different key, such as "Calendar", you can use a
-## tilde at the beginning of the mounted path to specify an
+## root of a ZODB is a PersistentMapping where Zope puts at least two
+## keys, "Application" and "ZGlobals".  If the important part of your
+## ZODB is stored under a different key, such as "Calendar", you can
+## use a tilde at the beginning of the mounted path to specify an
 ## alternative root name.  Example:
 
 # mount_paths=/MyApp/MyCalendar:~Calendar/
 
-## The auto_create parameter is useful for volatile data storage such
-## as DemoStorage or MappingStorage.  Since the database gets wiped
-## out when the process stops, the mountable object needs to be
-## created again upon restart.  auto_create does this automatically.
-
-# auto_create=0
-
-## The class_factory parameter selects what kind of class factory to
-## use in the database.  It exists to allow ZODB to handle missing
-## class instances gracefully and to support ZClasses.  The default is
-## "auto", which is good enough for almost everyone.
+## The container_factory parameter lets you choose what kind of
+## objects to create when mounting the database.  By default, DBTab
+## makes Folder objects in the mounted database, but you can use this
+## parameter to create PortalFolders or other kinds of objects
+## instead.  Its format is <product name>:<factory name>.  This
+## parameter is also useful when using volatile data storage such as
+## DemoStorage or MappingStorage.  Since the database gets wiped out
+## when the process stops, the mountable object needs to be created
+## again upon restart.  If you specify a container_factory, the
+## mountable object will be created automatically after restart.
+
+# container_factory=OFSP:manage_addFolder
+
+## Another example:
+
+# container_factory=CMFCore:manage_addPortalFolder
+
+## Note that container_factory replaces the old auto_create option,
+## which is deprecated as of DBTab 1.2.
+
+## The class_factory parameter is an advanced option that tells ZODB
+## how to import Python classes when loading objects from the
+## database.  The default is "auto", which is good enough for almost
+## everyone, but here are the other choices:
 
 ## - minimal: No handling of missing classes and no ZClass support.
 ## If ZODB attempts to load an instance of class it can't find, even