[TransWarp] Refactoring/renaming plans for peak.web
Phillip J. Eby
pje at telecommunity.com
Thu Jul 24 12:12:35 EDT 2003
This is a rough outline of the changes I'll be making in peak.web over the
next few days. As usual, comments and questions are welcome.
Traversal Terminology
---------------------
After analyzing and discussing the issues with locations being traversal
handlers, security guards, and presentation decorators, I've decided to
make the following name changes:
1. locationProtocol -> pathProtocol
2. behaviorProtocol -> pageProtocol
3. IWebMethod -> IWebPage
4. IWebLocation -> IWebTraversable
5. *AsMethod -> *AsPage
6. *Location -> *Traversable
IOW, there will be no such thing as a "location" any more; the term was
wrong. We want to have objects' fields (for example) be "traversable", but
they are not necessarily "locations". The interaction's protocol for this
will be called the 'pathProtocol', emphasizing that its purpose is how that
interaction will interpret paths.
There will be a new class, 'web.Decorator', that you will subclass in order
to provide "traversal adapters" for domain components. The idea is that
you define any UI or presentation-specific methods in the decorator
subclass, and declare it as an adapter for the domain class it
decorates. The base class will support IWebTraversable, so subclasses will
only need to define methods, bindings, and page templates as desired. When
traversing, the decorator's attributes will be checked first, and then
those of the adapted object.
Security
--------
After discussion with Ty, we decided not to introduce proxies at this
time. Traversables will still be required to handle security checks in the
same fashion as at present. Python code written in Decorator classes will
be considered "trusted" at the present time. (That is, able to violate
declared security rules by direct object access.)
binding.Attribute will be extended to support a 'permissions' keyword
argument, allowing security declarations to be made on bindings. Note that
this is advisory data only; if a Traversable or other class doesn't
actually check permissions using peak.security, these declarations will
have no effect.
We will also make it possible to declare permissions on plain ol' Python
methods of Components, probably via something like 'methodName.permissions
= [security.Anybody]' in the class.
There may be some refactoring of the security declaration APIs. Notably,
'security.allow()' will likely only be needed for non-Component classes, as
binding.Component will be able to implement IGuardedObject directly. Also,
it's likely that 'security.allow()' will be changed to use some sort of
"permission declaration" interface, instead of hacking into the class it's
used on.
DOMlet Rendering
----------------
Currently, the IDOMletNode.renderTo() method takes four arguments: the
interaction, a 'write' function, a traversable representing the current
target data, and an "execution context" object. My experience writing
DOMlets so far is that dealing with four arguments is awkward; I can never
remember their order or correct names.
I made these things arguments so that they can be changed by parent DOMlets
supplying them to child DOMlets. But, as it happens, they are not all
changed with the same frequency of occurrence. The target data, for
example, changes regularly, whereas the interaction and write function are
likely to never change at all. The execution context will only change when
a DOMlet wants to make some dynamic data or services available to
collaborating child DOMlets.
Thus, I would like to combine execution context with the interaction and
write function, wrapping all three in a new object called a "state", which
will be defined by an IDOMletState interface. The 'state' will have
'write' and 'interaction' attributes, and it will also "be" the old
"execution context" argument. There will be a simple DOMletState base
class used to bootstrap rendering.
Components that want to provide dynamic data or services to subcomponents
will create a new state instance with the previous one as its parent
component, which they'll then pass to their subcomponents. States will
probably have a 'findState(interface)' method, that can be used to find the
nearest state in the state chain that implements a particular
interface. The idea is similar to finding utilities in a component tree,
except that here the utilities *are* the tree.
So, the renderTo(...) method will become 'renderFor(data, state)', where
'data' is a traversable, and state is an IDOMletState carrying the
interaction and write function. In this way, changing the current data
element for child DOMlets is still easy, and to change anything else, a
DOMlet will just create a child state and pass it along. To supply a
special service or data to children, the DOMlet should have a state
subclass that implements an interface identifying the service, and the
children will use 'findState()' to retrieve it.
On a related note, some DOMlets may wish to provide data to their children
that's accessible via data paths in 'domlet' attributes. For example, we
might want the "list" DOMlet to allow access to things like a sequence
index. To do this, such a DOMlet would simply instantiate a traversable,
using the current data as its parent traversable, and then use that new
traversable as the parent when wrapping each item in the sequence, passing
it to the child components. Thus, the tree would look something like:
...
/
traversable for value being iterated over
/
traversable for sequence data
/
traversable for the current item in the sequence (passed to listItem DOMlet)
Thus, if the "sequence data" traversable had a 'sequence_index' field
containing the index of the current item, you could in your 'listItem'
definition have a table cell marked 'domlet="text:../sequence_index"',
which would go up from the item to the sequence-data traversable.
Notice that for this kind of data tree manipulation to be useful, a DOMlet
*must* document for the user exactly what it does to the tree. Otherwise,
users' paths in child DOMlets will be thrown off base. Also note that
another approach to this for the "list" DOMlet would be to just provide a
"sequence data" component as data, and require explicit reference to the
sequence item, ala DTML's 'sequence-item'. In practice, though, it'll
almost certainly be more common to need to reference data from the sequence
item, than to refer to values like the roman numeral version of the current
sequence index. :) So, I think we'll use the '..' approach for the "list"
DOMlet.
More information about the PEAK
mailing list