This is a follow up to the post Automating Documentation. Jim Priest wanted to see example code, and I’m happy to oblige.
For this example, I am sharing the code for documenting “steps” in Squidhead. Steps are operations that do one thing, like generate stored procs, or ant scripts, or email developers. They are powered by cfm templates in a specific folder. Once there they can be referenced through in the project’s config file. The documentation for them is included in the download for Squidhead, and can be viewed online (Squdihead – Steps).
So after the jump, here is the code for creating this documentation.
So first thing I do is setup a few parameters.
<cfset stepsXMLfile = “#expandPath(‘.’)#/stepsXML.xml”
/>
<cfset outputfile=“w:inetpubwwwrootsquidhead2docssteps.cfm”
/>
<cfdirectory
action=“list”
recurse=“false”
directory=“#expandPath(‘../../steps’)#”
name=“steps”
type=“file”
/>
Then I conditionally build the file in case I deleted it.
<h2>Building XML</h2>
<cfif
not
FileExists(stepsXMLfile)>
<p>Transforming Steps to XML.</p>
<cfxml
variable=“XMLOutput”>
<steps>
<cfoutput
query=“steps”>
<#ListFirst(name, ‘.’)#>
<documentation />
<requires />
</#ListFirst(name, ‘.’)#>
</cfoutput
</steps>
</cfxml>
<p>Writing to Disk.</p>
<cffile
action=“write”
file=“#stepsXMLfile#”
output=“#XMLOutput#”>
<cfelse>
<p>Already Built.</p>
</cfif>
Then I go through and check to see if each file has a documentation node:
<h2>Checking XML</h2>
<cffile
action=“read”
file=“#stepsXMLfile#”
variable=“rawFile”
/>
<cfset stepsXML = XMLParse(rawfile) />
<cfset changed = FALSE
/>
<cfoutput>
<cfloop
query=“steps”>
<cfset stepName = ListFirst(name, ‘.’)/>
<cfif
not
structKeyExists(stepsXML.steps, stepName)>
<p>Adding #stepName#</p>
<cfset changed = TRUE
/>
<cfset stepsXML.steps[stepName] = XmlElemNew(stepsXML,stepName) />
<cfset stepsXML.steps[stepName][‘documentation’] = XmlElemNew(stepsXML,“documentation”) />
<cfset stepsXML.steps[stepName][‘requires’] = XmlElemNew(stepsXML,“requires”) />
</cfif>
</cfloop>
</cfoutput>
<cfif changed>
<cffile
action=“write”
file=“#stepsXMLfile#”
output=“#stepsXML#”
/>
</cfif>
Next I go through and mark any documentation that is not filled in as a problem and through a 500 error. This will cause ANT to stop running.
<cfset incomplete = FALSE
/>
<cfset stepArray = StructKeyArray(stepsXML[‘steps’]) />
<h2>Checking Documentation</h2>
<cfoutput>
<cfloop
index=“i”
from=“1”
to=“#ArrayLen(stepArray)#”>
<cfif
StructKeyExists(stepsXML[‘steps’][stepArray[i]], “documentation”) AND
len(stepsXML[‘steps’][stepArray[i]][‘documentation’][‘XMLText’]) lt
1>
<p>Documentation for step #stepArray[i]# is not filled in. </p>
<cfset incomplete = TRUE
/>
</cfif>
</cfloop>
</cfoutput>
<cfif incomplete>
<cfheader
statuscode=“500”
/>
</cfif>
I finish by writing the XML back out to disk as HTML. I’m sure this can be done through XSLT, but I don’t bother.
<h2>Building HTML Reference.</h2>
<cfsaveContent
variable=“stepReference”>
<h2>Step Reference</h2>
<cfinclude
template=“stepsDocs.cfm”
/>
<cfset stepArray = StructKeyArray(stepsXML[‘steps’]) />
<cfset
ArraySort(stepArray,‘textNoCase’ )>
<h2>Step Details</h2>
<cfoutput>
<dl>
<cfloop
index=“i”
from=“1”
to=“#ArrayLen(stepArray)#”>
<dt>#stepArray[i]#</dt>
<dd>#stepXML[‘steps’][stepArray[i]][‘documentation’][‘XMLText’]#</dd>
<cfif
len(stepXML[‘steps’][stepArray[i]][‘documentation’][‘XMLText’]) gt
0>
<dd
class=“requires”>Requires: #stepXML[‘steps’][stepArray[i]][‘requires’][‘XMLText’]#</dd>
</cfif>
</cfloop>
</cfoutput>
</dl>
</cfsavecontent>
<cffile
action=“write”
file=“#outputfile#”
output=“#stepReference#”
/>
Then to call through ANT, I just add this line to a task in my ANT build file:
<get
src=“${project.url}/tools/stepDocumenter/”
dest=“${project.logPath}/tools/stepDocumenter.html”
/>
I then include these dynamic steps in a static document that includes some background on what steps are and what not.
Thanks for the followup! I’m going to tinker with this just as soon as I finish up my current project!
LikeLike
very informative, thanks
LikeLike