When I first started writing CGI stuff, the first thing I noticed is that casting HTML into a C CGI program seemed rather baroque. Aside from being exceedingly ugly, it is also really error prone and makes the development cycle painful. My first pass through this was to write an HTML interface library which abstracted HTML one level so that you don't, at least, have to look at hidious and repetitive printf()'s, but clearly this was still rather inadequate. The HTML interface library is still useful, but something more was needed.The HTML template interface was the solution. The idea is to keep HTML in plain old HTML documents, and create an interface library which digests those documents and allows the program to manipulate them. This is a far better solution because it completely separates the presentation end of things from the data manipulation end. If you have graphical layout and content creation folks, they can use their favorite HTML generators to create the content, completely separating it from code writers. Except...
One thing that because fairly obvious is that the code generation part generally needs some hooks to know where to bind up values to forms, say, or insert tables or whatever given the template. For this reason, pseudo tags were implemented. Pseudo tags resemble normal HTML markup tags, but all start with a dollar sign, "$". Unlike normal tags, psuedo tags manipulate HTML generation, and tell the CGI program information about the document. By making this extensible, pseudo tags now have the ability to mark a point in the document, include other documents, conditional expand parts of documents, etc, etc. The most groovy thing is that in conjunction with hrec's, creating form based programs is a breeze. The normal flow of things is to:
- Suck in the template
- Grab the form data from a database into an hrec
- bind the hrec to a particular form
- dump the page.
After creating the template manipulation routines, I noticed another ugly problem: the concept of a record -- typically a row from a database -- keeps coming up over and over again. Initially, I kept creating structures which decscribed the record. Beside being tedious, it was extremely annoying when it came time to either bind a database record to the form, or empty the contents of a submitted form. Clearly what was needed was to think of these records as name/value tuples and refer to them as such. Libht and the Record Interface
An hrec is exactly that: a collection of name value pairs. Internally, it is a hash table to insure fast access to the values. One key thing to note is that if you keep the names consistant from database table to output form, the hrec can be used to automatically bind the contents of your database record directy to and from the HTML form. This is performed by the function htbindform() to move data from the database to the form, and hrgetparams() to move the data from the form to the database. You might cry foul here knowing that the input form might have a lot more data than just what goes to/from your database, but not to worry: you can hide various fields so that they are invisible.
On the database end of things, moving data from the database to the hrec which can be later bound to/from the form is where the functions in the database interface routines come in play. The MSQL interface, for example, allows to you retrieve and store rows directly to/from hrecs. Setting up the columns is simply a matter of creating a template hrec with the fields you are interested in retrieving. Complex multi-table joins can be achieved by simply naming the hrec fields appropriately (ie, table.field). The function hrmerge() allows you to mask certain fields when copying from one hrec into another, which is sometimes neccesary to preserve values which the program is not allowed to modify (eg, a creation date which only allowed to be modified once). Hrecs, I've found, come in handy just about anywhere that you have row oriented data. This is certainly true of tables, and session management. There is even a simple linked list interface such that they can be chained together to create an internal representation of the records. It's not as smart as it should be (it would be nice have a faster search function like a hash table or some sort of tree, but that's for the future.)