[Zope-dev] BTreeFolder w/ Customizer support

Phillip J. Eby pje@telecommunity.com
Wed, 30 Aug 2000 15:46:25 -0500


At 09:24 AM 8/29/00 +0100, Steve Alexander wrote:
>Shane Hathaway wrote:
>
>> Bill Anderson wrote:
>> > Any chance at a BTree Folder w/customizer support in the near future? :)
>> 
>> It should be quite easy to do.  I vaguely recall Steve A or Steve S
>> might have done it.
>
>I want to include a BTreeFolder w/ Customizer support in the next 
>release of DataSkinAddons.
>
>If anyone's done one, and you're happy for it to be included in 
>DataSkinAddons, please let me know.
>
>Otherwise, I'll work on this later today.
>
>I've just produced a BTreeDataSkin:
>----
>BTreeDataSkin is a DataSkin that is an ObjectManager.
>The ObjectManager stores subobjects in a BTree that is directly in the
>object (not stored in an attribute provider).
>On __getattr__, subobject ids are searched before the datamanager is
>asked to provide attributes.
>----

If I may offer a suggestion...  it would probably be a lot cleaner to
implement this as an AttributeProvider, and use the dataskin's "persistent
data slot" to store the b-tree.  Otherwise, your b-tree contents can only
be used with DataSkins which are not stored "virtually" in a Rack.  If you
use the slot API, it will work with any DataSkin.  In other words, a
BTreeAttributeProvider which simply stores attributes in a persistent
BTree.  Such a thing would be useful independent of the BTreeFolder user
interface and could work with any DataSkin.

Here's some code that implements a similar notion (using a dictionary in
the DataSkin's storage slot, rather than a BTree).  It's from the version
of ZPatterns I'm currently working on.


eaKey = 'ZPatterns.AttributeProviders', 'ExternalAttributes'

class ExternalAttributeProvider(AttributeProvider):
    """
    This attribute provider stores persistent attributes outside the object
    itself, in its persistent 'slot'.  This allows persistent attributes to
    be used even with non-persistent DataSkins (e.g. those which are only
    virtually stored in a Rack.
    """

    def _AttributeFor(self,client,name,default=None):
        return client._v_readableSlot.get(eaKey,{}).get(name,default)

    def _SetAttributeFor(self,client,name,value):
        """Set the attribute and return true if successful"""
        attrs = client._v_readableSlot.get(eaKey,{}); attrs[name]=value
        client._v_writeableSlot[eaKey] = attrs
        return 1

    def _DelAttributeFor(self,client,name):
        """Delete the attribute and return true if successful"""
        if client._v_changedAttrs_[name] is NOT_FOUND: del
client._v_changedAttrs_[name]
        attrs = client._v_readableSlot.get(eaKey,{})
        if attrs.has_key(name):
            del attrs[name]
            client._v_writeableSlot[eaKey] = attrs
            return 1

    # Class Metadata
    
    meta_type='Persistent External Attribute Provider'


The code works by handling a persistent "slot".  Currently, slots are used
only by Persistent PropertySheet Providers to store propertysheets.  The
"_v_readableSlot"/"_v_writeableSlot" objects refer to a PersistentMapping
which is associated with the DataSkin.  By convention, the entries in the
persistent mapping are keyed with a tuple that is the module name and
purpose of the entry.  This is why things are written to
client._v_writableSlot[eaKey] -- eaKey is a constant that ensures that
other providers needing to use the slot for persistent storage will not
collide with it.  You could probably take the code above, and, with some
adaptation, produce a BTreeExternalAttributeProvider which used a BTree in
place of a dictionary to store the attributes.  You would need, of course,
to use a different constant tuple to key the slot.

_v_readableSlot always returns either a PersistentMapping, or an empty
dictionary if the DataSkin's slot has never been written to.
_v_writeableSlot always returns a PersistentMapping, ready to be written
to.  Once _v_writeableSlot has been accessed even once, the slot exists and
_v_readableSlot will return the same PersistentMapping object as
_v_writeableSlot.  This rather odd-seeming setup is to prevent accidental
changes to persistent storage when no changes have actually been made.