Code Generation
From BriansWiki
Contents |
[edit] Show me the code
- My Code: cftemplate.zip (updated 1/8/08).
- Peter's code: cftemplate.riaforge.org
- See Usage, if you must.
- Full sample project from generated code: CfArtGallery.zip (but that's cheating)
[edit] Back Story
After a year of writing the same applications over and over again I finally dug into the cfTemplate application from Peter Bell, I think the man is brilliant, the syntax for the template engine is just a few symbol substitutions in the ColdFusion command syntax which makes for great readability and adds that second level of abstraction that you need for a template to run.
[edit] Template Syntax
To define a tag, the template functions use a double "<" syntax so the output tag looks like this:
<<cfoutput>>Do something loopy<</cfoutput>>
Variables are similarly substituted, only now you replace the "#" with a "%" so for a reference to a piece of metadata you would do this:
%Metadata.ProjectName%
[edit] Template Generation
The output engine is simply ColdFusion's cfSaveContent() function but the trick is the reversal of tags and variables from the template to the generated code. Here's what Peter does as near as I can tell:
- First turn CF Template tags and variable identifiers into arbritrary strings e.g. "<<" becomes "!!START_CFTEMPLATE!!"
- Then turn ColdFusion tag and variable identifiers into arbritrary strings e.g. "<" becomes "!!START_CF_TAG!!"
- Turn CF Template tag and variable identifiers into ColdFusion tag and variable identifiers
- Save the transformed template to the scratchpad directory for parsing
- Run the template to generate code
- Delete any scratchpad files
- Transform the Coldfusion arbitrary strings code back to CF code to create the generated output
This is all easier demonstrated than explained, here is simple template to create an Index page for our We Three Divs Layout:
[edit] Template Example
<<cfoutput>>
<div id="navcontainer">
<ul id="navlist">
<<cfloop index="i" from="1" to="%arrayLen(Metadata.propertyArray)%">>
<<cfif %Metadata.propertyArray[i].typeName% eq 'int'>>
<<cfif %Metadata.propertyArray[i].name% neq '%MetaData.indexName%'>>
<li>
<a href="%Metadata.propertyArray[i].name%.cfm">
manage %Metadata.propertyArray[i].name%
</a>
</li>
<</cfif>>
<</cfif>>
<</cfloop>>
<li><a href="%MetaData.tableName%.cfm">main</a></li>
</ul>
</div>
<</cfoutput>>
[edit] Metadata
If you notice in the template we keep on referring to "Metadata"; this refers to the object the cfTemplateAction page created from all the index page's inputs and the table's "getMetadata()" returned properties. In the most abstract sense however, Metadata is is all the information about your project and the data that it depends on (to simplify to the extreme). This is the core technical challenge of code generation; how can I systematically describe a project's requirements and business logic so that I can build a template and combine it with the Metadata into something that fulfills the project's requirements.
[edit] Application Design
There are two files that really constitute the core of the cfTemplate application, the cfTemplateaction.cfm and the cfTemplate.cfc. The original is available from cftemplate.riaforge.org. I changed it to return more information about the table, specifically the datatype of the fields. This allows me to make assumptions about display options, all of which leads to the limitations of the template engine as it exists:
[edit] Limitations
These are the limitations I know of:
- Right now the templates only support the following data types:
- int
- int identity
- currency
- numeric
- char
- varchar
- nvarchar
- longchar
- smalldatetime
- datetime
NOTE: If your datatype for a field is not in the list the field will be left out of the generated code.
I am also not doing anything special for longChar fields, which should default to textarea input fields, and I also don't format currency - these features will be left as an exercise for the reader.
[edit] Assumptions
The assumption is that you want a 3 div app with search, list and detail forms, all connected with AJAX and using our best practices for variable naming, error handling and form validation. Here's the list of other assumptions:
- Your table's index is numeric
- Your DAO (aka "bean") has an init function that accepts an ID
- Your project is in the root on the local server (your computer)
- You use the ProcessForm method to handle a form submit
[edit] Naming Conventions
- Form field names are the same as table column names
- Table names have a three letter program designation prefix followed by an underscore (e.g. "TOS_")
- The DAO cfc will be the Table name plus ".cfc"
- The Gateway name will be the Table name plus "Gateway.cfc"
- The Index form name will be the Table Name plus ".cfm"
- The Search form name will be the Table Name plus "Search.cfm"
- The List form name will be the Table Name plus "List.cfm"
- The Detail form name will be the Table Name plus "Detail.cfm"
- Date field name for searches have "Start" and "End" suffixes
- Lookup tables have an index named "ID" and a display value called "name"
[edit] Usage
The code can download the code here: cfTemplate zip. Unzip to the root of your local test server and run the index.cfm. You should see the CF Template Code Generator form which has fields to enter your project name, DSN, table name, index field and link field (the field that links the list and the detail) If you are not creating the application.cfc you don't need to enter the AppID. If the output path you enter doesn't exist you will get an error when you run the generator so you better check that it is there. If you are not sure what ColdFusion calls your field's datatype, you can add a "&dumpMetatData=1" to the cftemplateAction.cfm url to see the array of fieldnames and their datatypes. If there is a datatype in your table that is missing the easy way to handle it is to simply convert it to one of the supported types, otherwise you will need to modify all the templates to recognize this type and behave appropriately.
When all that is set, choose from one of the available templates:
[edit] Templates
Your templates are obviously going to be different than mine. It has taken me a year to be able to understand the fundamentals of ColdFusion enough to create these templates and I borrowed heavily from the cfEclipse wizards and my co-workers. If you see something stupid in these, please let me know Brian Caufield
Here's all the templates I have so far, this should be sufficient to create an app to search, list create and edit the records in a database table:
[edit] CFC DAO
Based on the CFEclipse wizard and Catherine's adapations of it.
[edit] CFC gateway
Also based on the CFEclipse wizard, this component will return objects on get and init. It has a search function too, which I find handy for a gateway.
[edit] Edit Form
Simple table with all the fields and somewhat appropriate input types. The hard part is figuring out when you want a select, by default all integer fields (except the identity) are List boxes since adding a numeric-text input is easier than adding a select.
[edit] Action page
This is used by the detail form to set the Table values. It is completely abstract in the simplest use case and should be reused as an include if you have a lot of lookup table maintenance forms. In the complex cases, you may need to add app-specific logic.
[edit] Index.cfm
This sets up the 3 divs and defines the basic look of the main page. The default is to name the page the same as the underlying database table. If this is your main form, change it to "index".
[edit] List Form (Sortable Table)
Creates a sortable table that show the results of the search form. It includes pagination (first, next, previous and last buttons) including page position and total pages. The code for this feature was borrowed from the Dreamweaver table-grid wizard.
[edit] Search Form
Similar to the detail form but it adds ranges for any date field (DateBegin, DateEnd). The results of the search are displayed in the list form.
[edit] Application.cfc
This creates the core objects and sets up the request and application scopes. While there is only one version of this file for a project, I usually run it for every lookup maintenance page to get the form specific code for each lookup.
[edit] RequestHandler
This has all the app specific javascript for form handling. It also included a bunch of generic functions that can be re-used between different forms. I usually use the whole file for the main form and then just cut the form specific code out of the lookup maintenance pages.
[edit] Menu
This is a simple menu to call the lookup table managers.
[edit] Step by Step
- Download the scripts, images, styles and the sample database.
- Setup a DSN for the database called "cfArtGallery".
- Create a folder in the root of your local test server named "cfArtGallery" and unzip the scripts, images and styles into like-named sub folders. Then create "components" and "generated" sub folders for the cfc's and the generated output. If the "generated" folder does not exist you will get an error so you are for warned.
- First run all of the templates on your main table.
- Move your generated code to the correct location in your application, cfc's go in "components", scripts go in "scripts", the menu goes in "includes" and input forms are in the root. I usually compare the generated code with any previous versions to make sure I don't over write any modifications.
- Fix Lookup Table References: As noted in the limitations section, the templates assume that the names of lookup tables equals the integer field name in the main table. It also assumes that they all have an index called "ID" and a display field called "name". Unless you have made your lookup tables follow this syntax, you need to go through the Details and Search forms to correct these assumptions - the test database follows this convention to make things easier.
- Fix the createObject references in the Application.cfc for lookup tables. You only need to do this in the "onApplicationStart" function since the other references are all internally consistent. For instance if you have a lookup field called artistsID, the cfc will try to create a component called "artistsIDGateway", just change it to "artistsGateway".
- Change the table name in the Code Generation main page and generate the lookup table management forms, scripts and application.cfc. In our example we have two lookups; artists and media. Copy all of the forms into the appropriate folders but for the Application.cfc and the RequestHandler, just pull out the code specific to that form and add them to the main versions.
- Change the menu links to point to the main cfm's for the lookup tables like you did for the lookup components (e.g. "artistsID" gets changed to "artists").
[edit] Common Scripts
There are a couple of scripts that you need to include for all this AJAX to work: cfTemplate_scripts.zip, Here's how they get called but if you use the generated Application.cfc, these references are all already included.
<!--- load the calendar javaScript and style files ---> <link rel="stylesheet" type="text/css" href="../../tools/zpcal/themes/fancyblue.css"/> <script type="text/javascript" src="../../tools/zpcal/src/utils.js"></script> <script type="text/javascript" src="../../tools/zpcal/src/calendar.js"></script> <!--- Best Table Sort Ever ---> <script type="text/javascript" src="../scripts/SortedTable.js"></script> <script type="text/javascript" src="../scripts/Event.js"></script> <!--- form validation Script ---> <script language="JavaScript" src="../scripts/formVal.js" type="text/javascript"></script>
[edit] Common Images
Here are the images for the pagination controls in the list: cfTemplate_images.zip,
[edit] Common Styles
Here's the styles for that snazzy "We Three Divs" look: cfTemplate_styles.zip
[edit] Sample Data
Here's the database I used in testing the sample templates: Artgallery.zip It is basically one of the examples that ship with CF7 so you should have it already if you did a default install but I changed the lookup tables "media" and "artists" to use the default ID and name fields to make the demo easier.
