Tcl Procs
- Use namespace
Define your procs with a namespace like
mypackage::foo_proc
. Here is a discussion about [this]. Check many examples in the code, example:namespace eval auth {} ad_proc -public auth::require_login { {-level ok} {-account_status ok} } { doc... @return something @see ad_script_abort } { ... proc body }
- Use procs safely and their safer variations to help keep code robust and avoid security issues.
Particularly in cases, where user_input is processed, be sure to avoid executing unwanted code. Use the Tcl expand operator
{*}
instead ofeval
. Use
{*}$cmd
instead of
eval $cmd
For legacy code, you might use util::safe_eval instead of eval in such cases; subst_safe precedes meta characters with backslashes. - Use named parameters whenever possible
Define named parameters on your procs such that parameters will not be mixed up if somebody makes a mistake on passing the order of parameters. Also, this makes the proc easier to add additional parameters in the future if needed.
Use:
ad_proc proc_name { {-parent_id pid} {-child_id cid} } ...
and not
ad_proc proc_name {pid cid} ...
This way, developers will need to call proc stating explicitly which parameter are passed. This is especially useful when some parameters are optional.
Also, when calling a proc in your Tcl script, it is recommended to write one parameter per line like this:
set my_var [proc_name \ -parent_id $pid \ -child_id $cid]
Rather than:
set my_var [proc_name -parent_id $pid -child_id cid]
Again, this helps to make the code more clean and readable.
- Use ad_proc to define your Tcl procs
Make use of ad_proc. And make use of the self documentation facility of ad_proc.
ad_proc foo {} Use this area to document } # .... your implementation of proc foo }
This way, the API browser will pick up your documentation automatically. Is encouraged to use automatic api-doc documentation that ad_provides, such as:
@author
,@return
,@see
-
Use "ad_proc -private ..." always when a proc is used only in one package
This reduces the size of the public API and improves the flexibility of the package maintainers. -
Use "ad_proc -deprecate ..." when removing definitions from the public API
When deprecated code is called, the error.log of the site will show its usage. This way, a site maintainer can update with code with the new replacement call.
Don't move deprecated calls immediately to the long-range backward compatibility procs (tcl/deprecated-procs.tc). When OpenACS is configured to omit loading of long deprecated code (WithDeprecatedCode set to 0) these files are not loaded to reduce the every growing blueprint bloat. Therefore, these files should only contain code, which was deprecated at LEAST ONE RELEASE EARLIER, such that site admins have one release time to fix calls to deprecated code. - Avoid using "upvar"
Try to avoid using "upvar". If needed, pass in a parameter that specifies the "upvar" name. This way, the one using your proc has the option to name his/her variable. Example:
ad_proc upvaring {-upvar_name:required} { upvar $upvar_name local_var }
-
Use modern Tcl idioms
Do not use "
==
" in comparing strings. Using "if {$string == "foo"}
" tries to make a numeric comparison first. Instead, make use of "if {"foo" eq $string}
" or if you need the negation "if {"foo" ne $string}
".
Do not use "if {[lsearch -exact $list $element] > -1}
", but use "if {$element in $list}
" instead, or "if {$element ni $list}
" in case a "not in" test is required. -
Always "return" at the end of your proc
And if you have to return more than one variable, use associative arrays, which can be extended by additional fields without breaking code
So instead of this:
ad_proc ... { ..... return [list $creation_status $creation_message ...] }
use key/value pairs or Tcl arrays to group related information:ad_proc ... { array set creation_info { creation_status {} creation_message {} element_messages {} account_status {} account_message {} } ..... return [array get creation_info] }
- ... or even better: use Tcl dicts
ad_proc proc ... {} { set creation_info [dict create \ creation_status {} \ creation_message {} \ element_messages {} \ account_status {} \ account_message {} ] .... return $creation_info }
- Read the Tcl Style guide
This is the Tcl style guide (PDF), try to apply relevant guidelines. In particular chapter 4,5 and 7