Have you thought about using arrays? Very easy to make a single array to
hold all kinds of state info:
form(varname1,value) = the value
form(varname1,error) = the error
form(varname1,default) = default value
form(varname1,vproc) = validation proc
...
Then you only have to worry about passing around the form array. We do:
proc blah {_form} {
upvar $_form form
(mess with form array)
}
Very flexible since all your interfaces don't have to change when you want
to add a new capability.
Good luck!
Jim
>
> I haven't been able to find much help on this on the web, so I'm turning to
> the mailing list.
> We've been getting terribly frustrated with scoping and passing variables
> around in TCL. I have a form that accepts a lot of inputs and am calculating
> stuff based on those inputs. My problem basically is I want to avoid passing
> all the variables around to all the procs (especially procs that don't even
> use them and just pass them on somewhere else!) or making a global copy of
> every variable.
>
> I think I don't understand uplevel, upvar, global and namespaces etc
> properly, so it would be great if somebody could direct me to some help.
>
> Here's my problem:
> If I use uplevel around a chunk of code, I find it ignores parameters passed
> into the procedure.
>
> Here's what I've been doing: assume I have a main procedure which has
> variables x, y and z.
> I call test2 from this procedure (w is not a variable in the main
> procedure).
>
> test2 is defined as:
> proc test2 {w} {
> uplevel {
> set result [expr w + x +y +z]
> return $result
> }
> }
>
> This proc breaks because w does not exist on the level above.
>
> I then tried to use upvar:
> proc test2 {w} {
> upvar w w
> uplevel {
> set result [expr w + x +y +z]
> return $result
> }
> }
> This works fine if I call test2 once, but if I call it a 2nd time, I get an
> error telling me w already exists. Unsetting didn't work either because it
> was a link.
>
> My colleague came up with the following fix using namespaces which seems to
> work - he redefines the ACS procedure set_form_variables.
> (apologies to non-ACS people for including ACS code here ;-) )
>
> Is this the best solution? I don't like having to refer to the namespace
> everywhere I need to access x, y and z. Surely there must be a solution
> using uplevel etc.?
>
> I think I really need to learn more about all this kind of stuff - any
> pointers?
>
> cheers,
> Brian
>
> Call this and have it accept product_id with some value.
> ----Cut here----
> > ad_proc ba_set_form_variables {{error_if_not_found_p 1}} {
> >
> > Exactly the same as set_form_variables, except it sets the variables
> > in a namespace called "ba_ns"
> >
> > }
> > if { $error_if_not_found_p == 1} {
> > uplevel { if { [ns_getform] == "" } {
> > ns_returnerror 500 "Missing form data"
> > return
> > }
> > }
> > } else {
> > uplevel { if { [ns_getform] == "" } {
> > # we're not supposed to barf at the user but we want to
> > return
> > # from this subroutine anyway because otherwise we'd get an
> > error
> > return
> > }
> > }
> > }
> >
> > # at this point we know that the form is legal
> > # The variable names are prefixed with a V to avoid confusion with the
> > form variables while checking for naughtiness.
> > namespace eval ba_ns {
> > set Vform [ns_getform]
> > set Vform_size [ns_set size $Vform]
> > set Vform_counter_i 0
> > while {$Vform_counter_i<$Vform_size} {
> > set Vname [ns_set key $Vform $Vform_counter_i]
> > set Vvalue [ns_set value $Vform $Vform_counter_i]
> > check_for_form_variable_naughtiness $Vname $Vvalue
> > set $Vname $Vvalue
> > incr Vform_counter_i
> > }
> > }
> > }
> >
> >
> > proc test1 {} {
> >
> > set product_id "you won't see this either"
> > return $::ba_ns::product_id
> >
> > }
> >
> > set product_id "you won't see this"
> >
> > ba_set_form_variables
> >
> > doc_body_append "[test1]"
> ---end----
>