Beautiful Code [187]
A series of library routines allows the script to read and write variables in the cart's hash. As the script exits, it updates the database tables with the current contents of the cart. If the user does not have cookies enabled, he will still be able to interact with Gene Sorter during a single session because the session key is not kept in cookies. Separating the session from the user keys also lets the user have the Gene Sorter going in two separate windows, without the two windows interfering with each other. The user level of the cart allows the Gene Sorter to start up in the same place it was last used, even after the user has moved onto another site in the meantime.
In the http://genome.ucsc.edu implementation of the Gene Sorter, all of the CGI scripts on the site share the same cart. Thus, the cart contains variables that are even more global than normal program variables. This is often useful. If the user is focusing on the mouse genome rather than the human genome in one of our programs, she probably wants to be using the mouse on other programs as well.
However, as our programs have grown, to avoid inadvertent name conflicts between cart variables, we have adopted the convention that cart variables (unless they truly are meant to be global) start with the name of the CGI script that uses them. Thus, most of the Gene Sorter's cart variables start with hgNear_. (We use the underline character rather than a period as a separator because the period would interfere with JavaScript.)
All in all, the cart makes it relatively straightforward for the Gene Sorter to maintain the illusion of continuity to users, even though a separate instance of the program runs on each user click.
The short lifetime of a CGI script does have some advantages. In particular, a CGI script does not need to worry much about memory leaks and closing files because these are all cleaned up by the operating system on program exit. This is particularly nice in the C language, which does not have automatic resource management.
The Design of the Gene Sorte > A Little Polymorphism Can Go a Long Way
13.3. A Little Polymorphism Can Go a Long Way
Inside most programs of any flexibility, there is likely to be a polymorphic object of some sort. The table that takes up most of the main page of the Gene Sorter is composed of a series of polymorphic column objects.
Making polymorphic objects in C is not as easy as it is in more modern object-oriented languages, but it can be done in a relatively straightforward manner using a struct in place of an object, and function pointers in place of polymorphic methods. Example 13-1 shows a somewhat abbreviated version of the C code for the column object.
Example 13-1. The column structure, a polymorphic object in C
Code View: Scroll / Show All
struct column
/* A column in the big table. The central data structure for
* hgNear. */
{
/* Data set guaranteed to be in each column. */
struct column *next; /* Next column in list. */
char *name; /* Column name, not seen by user. */
char *shortLabel; /* Column label. */
char *longLabel; /* Column description. */
/* -- Methods -- */
void (*cellPrint)(struct column *col, struct genePos *gp,
struct sqlConnection *conn);
/* Print one cell of this column in HTML. */
void (*labelPrint)(struct column *col);
/* Print the label in the label row. */
void (*filterControls)(struct column *col,
struct sqlConnection *conn);
/* Print out controls for advanced filter. */
struct genePos *(*advFilter)(struct column *col,
struct sqlConnection *conn,
/* Return list of positions for advanced filter. */
/* Lookup tables use the next few fields. */
char *table; /* Name of associated table. */
char *keyField; /* GeneId field in associated table. */
char *valField; /* Value field in associated table. */
/* Association tables use these as well as the lookup fields. */
char *queryFull; /* Query that returns 2 columns key/value. */
char *queryOne; /* Query that returns