[TransWarp] peak.web and forms

Phillip J. Eby pje at telecommunity.com
Tue Aug 5 10:31:12 EDT 2003


At 02:41 PM 8/5/03 +0200, Roché Compaan wrote:

>I haven't looked at Resources yet, how do you make a WidgetSet a
>Resource? I just scanned your latest checkin messages and thought
>Resources are limited to files and templates - I must still study your
>resource design notes and the code.

A resource is a traversable component that is located at a fixed path 
relative to the skin.  It doesn't have to be filesystem-based, although all 
the current resource classes are.  All you have to do to get something into 
the skin is have it accessible by a path from one of the layers in the skin.

Let me back up.  Skins are composed of layers.  Layers are just 
traversables.  The skin is selected by a skin service.  The default skin 
service creates a skin with one layer, a DefaultLayer 
instance.  DefaultLayer traverses to package names, and uses the packages' 
directories to make sub-traversables.  But, you can also use a 
ResourceDirectory as a a layer, and put subdirectories in it, named for 
packages.  So you can create a directory, put a 'peak.web' (NOT peak/web!) 
subdirectory in it, and any peak.web resources will be drawn from that 
directory (unless there is an overriding resource in a higher-priority layer).

So, if you wanted to do some non-filesystem resources, you could create a 
traversable and put it as a layer in a skin.  It would need to traverse 
first to package names, and then those sub-traversables would contain the 
resources.

Now that I've explained all that, I should mention that I was assuming that 
the easiest way to do all that would be with files and directories 
anyway.  E.g., if you have a '.ws' file extension for widget sets, then you 
just put e.g. 'contacts_editForm.ws' in the package directory with the 
module that needs it, and use bindResource('contacts_editForm') (note the 
lack of extension; this is important for ability to override the type of a 
resource in a different layer or skin).

To define the mapping from .ws file to WidgetSet, you would add a 
'resources.ini' file to the module directory, and do a section like:

[Files *.ws]
file_factory = 'some.module.with.WidgetSet'
permissions = [security.Nobody]

The WidgetSet class (not its instances!) needs to implement 
naming.IObjectFactory; you can see FSResource or storage.ManagedConnection 
for examples of how to do that.  Basically you add a classmethod 
getObjectInstance and declare that classProvides=[naming.IObjectFactory].


> > Hm.  I'd rather that the view simply assigned the fields to the underlying
> > object, one after the other, tracking any errors.  After all fields were
> > set, calling an overall validate() method could be done on the object to
> > get any high-level issues.
>
>And validate can set an error message on the appropriate widget for
>rendering by the template.

Hm.  To be precise, I think you'd simply be creating a data structure with 
the messages, that the widgets would pull the data out of, rather than 
modifying the widgets themselves.


> > Mostly good, but there are some pieces I'm having trouble wrapping my head
> > around, like where does the POST go to?  What if there's more than one 
> form
> > on the page?  (These two questions overlap a bit, since if there's more
> > than one form, they can't both POST to the same place.)  If you POST to 
> the
> > widget set (a logical alternative), how do you get back to the template
> > that rendered the submitting page?
>
>I think this is really a pattern that each app should work out for
>itself but what follows is what I would like to do. I am not sure yet
>how this would work in PEAK but the POST goes to any method that keeps
>the form stuff in play eg. (I'll try to explain this in PEAK terms): I
>want to add, edit and view contacts TTW. I create an IWebTraversable
>that has add, edit and view methods - lets call them actions. 'add',
>'edit' and 'view' returns the templates 'add_template', 'edit_template'
>and 'view_template' when called for the first time or when we come from
>a given location. Each of the contact templates are coded to post back
>to their corresponding action on the contact traversable.

Hmmm...  here's an interesting thought...  I could make the 
CallableAsWebPage adapter look for a 'templateResource' attribute on a 
method, and then pass the return value of the method as input to the 
template in some way.  So you'd do, e.g.:

     def add(self, REQUEST):
         ...

     add.templateResource = "contact_addForm"

Of course, I'm not sure that this really saves anything.  It may be that I 
should just work out how to easily invoke a template from inside a method 
on an object.  One thing that talking about this scenario has brought up, 
is that there's currently no way to get at the traversal context from 
inside a method like this, so that makes things harder.  Otherwise, you 
could just say something like:

         return context.subcontext('addForm', self.addForm).render()

or:
         return context.contextFor('addForm').render()

depending on whether 'addForm' was publicly accessible.  Hmm, technically 
the above should both be context.getParentComponent().whatever, as the 
context would be the method, not the owner of the method.  So you'd have to 
go "up one", then "over".

Clearly, I need to have a way to allow the traversal context to be passed 
to Python methods; probably this will be as simple as setting a value in 
the request, that you can then use as a parameter name.  Maybe 
CONTEXT?  traversalContext?  ctx?  Maybe CONTEXT for the context and 
SUBJECT for the context that references the object whose method it 
is?  I'll think on this.




More information about the PEAK mailing list