[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