Forum OpenACS Improvement Proposals (TIPs): Enhance <multiple> to enable locally-aliased and implicit multirow reference styles.

SQL has a feature that allows table names to be aliased. For example:

select t.*, …
  from a_table_with_a_really_long_name t, another_table …

This makes it much easier to unambiguously refer to the columns of a_table_with_a_really_long_name. This is very useful in systems like OpenACS where the use of long descriptive identifiers is encouraged and pervasive. I propose we emulate the SQL standard and allow this capability within the <multiple> ADP element in acs-templating. Furthermore, since the empty-string is a valid name for arrays in TCL, we can also provide the opportunity to alias the multirow to "". This enables an implicit reference style in addition to the aliased one. Here is some ADP demonstrating all 3 reference styles for a multirow containing 3 columns (foo_id, name, and description):

<multiple name="table_of_foos" as="foo">
<li>@foo.foo_id@ -- -- @table_of_foos.description@</li>

This TIP can be accomplished by modifying 3 lines and adding 1 line in acs-templating, as seen in this unified diff:

Index: parse-procs.tcl
--- packages/acs-templating/tcl/parse-procs.tcl
+++ packages/acs-templating/tcl/parse-procs.tcl
@@ -591,7 +591,7 @@
   @author Peter Marklund (
   @creation-date 25 October 2002
 } {
-  return {(^|[^\\])@([a-zA-Z0-9_:]+)\.([a-zA-Z0-9_\.:]+)@}
+  return {(^|[^\\])@([a-zA-Z0-9_:]*)\.([a-zA-Z0-9_\.:]+)@}
 ad_proc -public template::adp_array_variable_regexp_noquote {} {
@@ -600,7 +600,7 @@
   @author Dirk Gomez (
   @creation-date 12 February 2003
 } {
-  return {(^|[^\\])@([a-zA-Z0-9_:]+)\.([a-zA-Z0-9_:\.]+);noquote@}
+  return {(^|[^\\])@([a-zA-Z0-9_:]*)\.([a-zA-Z0-9_:\.]+);noquote@}
 ad_proc -public template::adp_array_variable_regexp_literal {} {
Index: tag-init.tcl
--- packages/acs-templating/tcl/tag-init.tcl
+++ packages/acs-templating/tcl/tag-init.tcl
@@ -154,6 +154,7 @@
   set startrow [template::get_attribute multiple $params startrow  0]
   set maxrows  [template::get_attribute multiple $params maxrows  -1]; #unlimit
   set delimiter [template::get_attribute multiple $params delimiter ""]
+  set as		[template::get_attribute multiple $params as $name]
   set tag_id [template::current_tag]
@@ -173,7 +174,7 @@
   template::adp_append_code " } { incr $i } {
-    upvar 0 $name:\$$i $name
+    upvar 0 $name:\$$i $name $name:\$$i \"$as\" $name:\$$i \"\"
   " -nobreak
   template::adp_compile_chunk $chunk

The main anticipated problems have to do with nested multiples. Accounting for that possibility would expand the number of lines needed in the patch somewhat (perhaps about 8-12 lines).

I have applied this patch to one of our projects and anticipate it will go into production soon.

Hi Andrew

I think it's a good idea, but it strikes me that the real benefit it provides is the possiblity of aliasing the multirow to "". I imagine that most people would just use that (e.g. in your example), and probably never the other ways, as it's much terser.

Unlikely in practice, but hypothetically what happens if there's an existing multirow element called .name?

Supporting nesting would be critical I think.


i see the empty name as well skeptical. Having myself used the "empty array name" idiom in several places internally, i would rather plead for good and declarative names as "contract" between tcl-code programmer and adp-designer.

Similarly, i am wondering where the need for variable alias comes from. why don't you name the variables at first hand reasonably? it open more questions: what happens/should happen if the alias name shadows an existing variable name; or when one alias name is used for multiple vars, etc. Does the alias lead to better and easier understandable adp files? Just wondering and asking....


Regardless of the changes, a multirow column called .name would be handled in the same way as is currently done: you'd have to double the period:

If you are referring to the name of the multirow itself, the built-in regular expression before (and after) this change is not able to handle:

… because the regexp clause:


… will not accept a period anywhere between the @ and the . that separates the multirow name from the column name.

Nesting in the face of multirow name conflicts is uncommon, but it can be supported in a similar fashion to the existing support for nesting.


As a TCL programmer, I am naming the multirow variables very clearly and carefully. In complicated pages I am using long descriptive names for my multirows so it is clear to the designer what they contain. But the long descriptive names can obscure the visual structure of the ADP to the designer by creating a repeated pattern that does not convey useful information. This repeated information still takes up a lot of space. So a locally-defined aliased name becomes useful to remove the redundancy while maintaining minimal context. This helps reduce boiler-plate code and encourage concise expression of purpose.


Sorry, I forgot to respond to your other questions:
"… what happens/should happen if the alias name shadows an existing variable name[?]; or when one alias name is used for multiple vars[?] …"

I'm not sure what you are referring to by the second question. Can you please elaborate on it?

The rest of the post will deal with your first question.

While I am sympathetic to your concerns (the prospect of silently-clobbered and shadowed variables give me mental itch!), I think shadowing variables is an acceptable outcome here. OpenACS does not make heroic efforts to protect the programmer from shadowing (or even clobbering) their variables. For a long time db_multirow has silently over-written variables when it might have shadowed them instead. Consequently I feel it is slightly unfair to pick on this change for clobbering a variable that happens to be named the same as the alias.

The patch under discussion does not address clobbering of existing variables that match the name of the alias or the empty-name. It may be worth addressing this by making the alias-name shadow the existing variable as is done earlier in the code generated by the same template_tag proc:

if {\[info exists $name\]} {
    upvar 0 $name __${tag_id}_swap


if {\[info exists __${tag_id}_swap\]} {
    upvar 0 __${tag_id}_swap $name

Considering this is a problem that shows up at least 3 times, it might be worth implementing a small TCL API that is similar to upvar that will shadow existing variables rather that clobbering them. Perhaps they could be called pushvar and popvar and have the same signature as upvar (… ?level? otherVar myVar ?otherVar myVar ...?…)