proc name { list_of_arguments } {
body_expressions
}
This creates a procedure with the name "name." Tcl has a global
environment for procedure names, i.e., there can be only one procedure
called "foobar" in a Tcl system.
The next part of the syntax is the set of arguments, delimited by a set of curly braces. Each argument value is then mapped into the procedure body, which is also delimited by curly braces. As before, each statement of the procedure body can be separated by a semi-colon or a newline (or both). Here's an example, taken from a calendar widget component:
proc calendar_convert_julian_to_ansi { date } {
set db [ns_db gethandle subquery]
# make Oracle do all the real work
set output [database_to_tcl_string $db \
"select trunc(to_date('$date', 'J')) from dual"]
ns_db releasehandle $db
return $output
}
Of course in the actual ACS, we encourage programmers to use
proc_doc
instead, which defines the procedure but also
records a doc string, as described in array variable chapter. Here's what
the definition would look like with proc_doc
:
proc_doc calendar_convert_julian_to_ansi { date } "Return an ANSI date
for a Julian date" {
set db [ns_db gethandle subquery]
# make Oracle do all the real work
set output [database_to_tcl_string $db \
"select trunc(to_date('$date', 'J')) from dual"]
ns_db releasehandle $db
return $output
}
global
)
ns_share
)
To use a variable locally, you need not declare it. To instruct the
Tcl interpreter to read and set a variable in the global environment,
you must call global
every place that the variable is
used. For example, when side graphics have been displayed on a page,
ad_footer
needs to know so that it can insert a <BR
CLEAR=RIGHT>
tag.
# a proc that might display a side graphic
proc ad_decorate_side {} {
# we use a GLOBAL variable (shared by procs in a thread) as opposed to
# an ns_share (shared by many threads)
global sidegraphic_displayed_p
...
set sidegraphic_displayed_p 1
}
proc ad_footer {{signatory ""}} {
global sidegraphic_displayed_p
if [empty_string_p $signatory] {
set signatory [ad_system_owner]
}
if { [info exists sidegraphic_displayed_p] && $sidegraphic_displayed_p } {
# we put in a BR CLEAR=RIGHT so that the signature will clear any side graphic
# from the ad-sidegraphic.tcl package
set extra_br "<br clear=right>"
} else {
set extra_br ""
}
return "
$extra_br
<hr>
<a href=\"mailto:$signatory\"><address>$signatory</address></a>
</body>
</html>"
}
One of the strangest and most difficult to use features of Tcl is the
ability to read and write variables up the calling stack with
uplevel
and upvar
.
proc ad_header {page_title {extra_stuff_for_document_head ""}} {
set html "<html>
<head>$extra_stuff_for_document_head
<title>$page_title</title>
</head>
"
return $html
}
If a page calls ad_header
with one argument, it gets the
standard appropriate HTML header. If a page supplies the extra
optional argument, that information gets written into the HEAD.
Otherwise, the default value of empty string is used for the second
argument.
args
in
any procedure definition. After all of the other (previous) arguments
are bound to names, the rest of the arguments are shoved into a list
called args
which can then be accessed inside the procedure.
You could imagine that between optional arguments and extra ones, the interpreter might get confused. It doesn't, because it assumes that you aren't using extra args at the end without binding all of the optional ones in the middle; that is, it stuffs argument values into the argument names in strict order without regard to options, extras, etc.
rename old_name new_name
. If
you wanted to keep the old proc but still take advantage of the great
abstraction you've used throughout your site (i.e. not change all of
the command calls when you change their bodies), you can rename the
old proc and create a new one with the old one's name. You can
also use rename
with the empty string for the second
argument. This results in the proc being deleted.