[TransWarp] peak.naming .. first contact
Phillip J. Eby
pje at telecommunity.com
Sat Feb 15 12:01:27 EST 2003
At 10:56 PM 2/14/03 +0100, Ulrich Eck wrote:
>in the case of imap-folder i'ld need to access two attributes:
>- ACL: IMAP-ACL's are basically a list of tuples [('user', 'rights',), (...),]
> that are managed in my current implementation through a customized
>- Flags: Flags is a list of Names e.g. 'Marked','Noselect' that can be
>read for each folder.
>one could use it like this (just a tiny draft):
>acl = folder.getAttribute('acl')
>acl['someotheruser'] = 'lrwpicdsa'
>the underlying ContextImplementation would need to know how to handle
>In case of an LDAP-Context this could be very usefull as well, cause any
>can have children and attributes at the same time.
Yes, in JNDI the attribute functions are part of the "directory context"
interface, which is intended for directory services like LDAP. So
attributes are multi-valued lists. Of course, for LDAP, the values in the
lists are always strings in some sense.
I don't believe that a getAttribute or setAttribute function make sense;
they need to be getAttributes() and modifyAttributes(), with the former
allowing a list of attributes desired, and the latter taking a list of
modification operations. I guess I just need to take another look at the
interface package and see about defining them. The main open question
there is how to format the parameters, I suppose.
>of course one should be able to add Folders to the collection and _save()
>i'm not 100% clear about your description with the relationships.
>what do you mean by "the QueryLink should be on the "parent" pointer side
>of the relationship" ??
Meaning that if you want the _save() to look at the folder collection, then
you shouldn't be using a QueryLink there. If the _save() will work by
looking at an "owner" or "container" or "parent" reference that says what
folder this folder belongs to, then the QueryLink goes on the collection side.
Here's a question: can IMAP folders move? If not, then the simplest thing
to do is to give folders a "parent" model.Reference that refers to their
parent folder. Your _new() method will use this to know where to add the
folder. Even if folders *can* move, you can still save them in their moved
position using that parent reference.
This may be completely irrelevant to what you're doing, if you're using
folder names to determine their location, in which case it doesn't matter
where you put the QueryLink objects.
But, I don't recommend using data values that can change (folder path) as
an oid. If folders can't move, then you could use the path as an oid.
>how would one basically setup the relations to be able to use
>together with DataManagers ?
The model is independent. Just define the object model in an "idealized"
way, how you would want to work with and think about the objects in a
perfect world. Then, and only then, define the datamanager's
mapping. Otherwise, you will carry imperfections through from your
underlying mechanism to the object model, which eliminates the point of the
exercise in the first place. :)
As for the mapping, well that is dependent on the underlying mechanism.
>in the old TW.Database i did manual querying and updates on the DM from
>within the features
>i'ld like to be able to do something like this:
># setup stuff to use IMAP_DM
># retrieve folder and use it
>folder = IMAP_DM['INBOX.test']
><prints out a list of instances of child-folders>
>[('test1', 'lrpwicdsa',), ('test2', 'lrwpicdsa',)]
># modify folder
># an acl may get an object as well .. don't know yet.
># create a subfolder
># this below is not mandatory if there is a cleaner way to do
>new_folder = IMAPFolder(IMAP_DM, componentName='sub01')
Er, don't call it 'componentName'; for Element objects that sets the
_p_oid. You need to make that field part of the model itself, as a field
for say "name" or "folderName". And do it like this:
# unless IMAPfolder is the default, in which case leave it out
new_folder = IMAP_DM.newItem(IMAPFolder)
new_folder.name = 'sub01'
># storing stuff on commit
># an imap-connection is not really transactional
># so it may not make sense - does it ??
That should be storage.commit(IMAP_DM)... it might coincidentally work
because you only have one transaction, but if IMAP_DM is using a different
transaction service than the global one, that commit will fail.
I would suggest that you have your DM raise an error when it is asked to
abort, if it has already committed anything. Something like:
def abortTransaction(self, txnService):
savedCount = len(self.saved)
# do normal abort cleanup
raise SomeKindOfError("Already saved %d objects to IMAP" %
You see, objects aren't saved to the underlying storage until the 'flush()'
method is called. This method is ordinarily called at the pre-voting phase
of transaction commit, which is part of the 'storage.commit()' call. So
yes, it does make sense to use transactions with a data manager. In fact,
Entity DMs *must* be in a transaction, they're not built to work any other
way. (Query DM's can in principle be run without transaction
support.) Anyway, as long as the transaction doesn't abort *during* the
commit attempt, everything will be fine. And the transaction machinery can
be hooked to do something special for partial aborts, like logging it,
shutting down, etc.
The other time that 'flush()' would be called is if a co-operating QueryDM
wants to ensure that the external state matches the cache state. So if you
have a QueryDM that retrieves a list of folders, for example, then it would
want to call 'flush()' on the DM that handles adding or modifying folders,
so that the IMAP server would be up-to-date with respect to what has been
done in the transaction.
You'll notice that this means that it might be a good idea to use different
EntityDM's to handle folders as distinct from messages. Even if you use
the same DM "behind the scenes", it's probably good to stick in some
FacadeDM's so that your code only sees "user-friendly" primary keys.
The whole purpose of the peak.model and peak.storage systems is to present
a friendly face on your "back end" systems. It's a bad idea to expose the
inner workings of these things to application-level code. Your view code
(if you're using Zope 3) should only have to deal with user-friendly keys
issued to a DM (that may be a Facade or not, but the UI shouldn't care) and
with the interfaces exposed by the model instances.
>that commit would do the following:
>- store the modified acls for folder
>- use folder.path + [new_folder.getComponentName()] as imapFolderPath
> in this case: 'INBOX.test.sub01'
> and create the subfolder in the imap-server and set's acls like defined
>for the ACL set/get it's fairly easy to fetch them at <dm>._load() time
>and store them when _save() is called.
>how should this be done for subfolders ??
You lost me here. Folders are folders, aren't they? What's the difference?
If you mean, "how can I ensure that a folder's parent is created before it
is created", then what you do is in your _new() method, you call
'self.oidFor(ob.parent)' to ensure that the parent gets an oid assigned
(presumably, by adding *its* parent's oid to its name part). Something like:
ob._p_oid = '%s.%s' % (self.oidFor(ob.parentFolder), ob.name)
# do whatever's needed to save the new folder
# possibly including a call to self._save(ob) if its code is reusable...
Anyway, the first line there will create an oid that's the full folder
path. In practice, you may want a folderPath attribute in your object
model; if so, you can always use that in your _new() method rather than
recalculating it, but be sure to call the 'oidFor()' check if there's a
chance that the folder's parent hasn't been saved yet.
Note that the way that an EntityDM determines whether an object exists in
external storage is by whether it has a non-None _p_oid. If you assign the
oid ahead of time, _new() is *never* called for that object. _new()'s job
is to assign the oid and ensure the object exists in external storage.
More information about the PEAK