Camen Design

c share + remix

ASP Templating

'//Allows replacing of tags in a text file with other content
Class clsTemplate
	Public Content  '//The text of the template
	Private Sub Class_Initialize
		Content = ""
	End Sub
	'//Substitute a tag marker "&{TAGNAME}" with given content
	Public Sub ReplaceTag (ByVal strTag, ByVal strContent)
		'//Whilst tags and constants are functionally the same, they allow you to markup your templates
		'//in a way where it is clear that tags represent large substitutions and dynamic content, and
		'//constants represent highly common, short one-line strings, such as HTML IDs, URLs and the like
		Content = Replace (Content, "&{" & strTag & "}", strContent)
	End Sub
	'//Substitute a constant marker "&__TAGNAME__" with given content
	Public Sub ReplaceConst (ByVal strConst, ByVal strContent)
		Content = Replace (Content, "&__" & strConst & "__", strContent)
	End Sub
End Class

'//Retrieve a template file and then provide direct access to it
Public Function LoadTemplate (ByVal strTemplateName)
	Set LoadTemplate = New clsTemplate
	LoadTemplate.Content = App.Disk.LoadText (TemplateDir & strTemplateName)
End Function

This took over 100 hours to create.
No, seriously, it did.

I’ve been going over a website I designed and created a while ago with a fine tooth comb, rectifying some of the ancient and frankly awful code. One of the biggest problems outright was that of 12’500 lines of code in the site, the HTML code was totally intermixed with the server side code. Updating anything was a total pain, and the logic code was so spread out it was proving a nightmare to re factor it.

The solution to this would be to template the HTML content and simply swap out some tags in the template with the dynamic parts of the content. The real challenge in doing this was being able to deal with a website with pages containing very intermixed dynamic, and non dynamic content, plus lots of common HTML used between all the pages.

I needed a way to recursively handle templating, being able to swap out one template tag with the content of another template, and then deal with the dynamic parts of that sub template, ad-infinitum. This posed a real head-racking problem trying to piece together the concepts in my mind, with such a limited programming language, and still trying to maintain some elegance. It’s like trying to solve algebra. You have x and y, but neither are concrete known numbers yet, but you still have to do multiplication with them. Needless to say I paced around a lot, went through a lot of cups of tea.

Before I started, I had googled quickly to see what else people were doing regards templating in ASP. I looked at a class called “ASPTemplate” which included the basic concept of swapping out markers in a template file with defined “tags”, allowing you to put a {TAGNAME} marker in the template, and then replacing it with your dynamically generated content before going to the presses.

It was over engineered though.
Look at my solution, then check out the 530-line ASPTemplate

There is an art to classes and that is do one thing, and do it well. ASPTemplate was using regular expressions to find each tag in the template, and then substituting the defined tag content if present. The problem with this is that it was looking at all tags on the page, and trying to find content for each, meaning that there was added complexity dealing with tag markers which then had no content given to the class by the caller. This is just bad design.

With each template marker tag, you’re making a statement that this will have some content. There isn’t anything fuzzy about this. Being able to ignore tag markers would lead to severely lazy templating, potentially causing very hard to trace bugs when you start get into recursive templating with dynamic and non dynamic content replacement.

ASPTemplate had the problem of trying to cater to everybody, including lazy programmers with bad code. In order to be elegant I needed to be able to define one clear way of templating that made precise design statements about how template files should be written. This is what I struggled with a lot, trying to hone the code down to a single precise statement.

About half way through development I had produced a class that created two arrays Tags and Constants, that the caller added content to with the relevant tag name. Then an Evaluate function would be called that then looped through each tag replacing the content and returning the resultant string. This way only the tags the caller added would be parsed, meaning that any tags that were not defined by the caller, but were present in the template file would be clearly visible on the output.

Whilst I had something that worked like ASPTemplate, it still wasn’t elegant. The Evaluate function was side-effecting by acting upon the .Content property, but also then returning the property as well.

Also I was having difficulty pinning down who would handle the recursion. Would this be handled in-class by allowing special tags that would auto load and replace template files? The problem was that I was still trying to do too much. I had seen ASPTemplate, and thought it the style to follow.

Firstly, if I was only storing the replacement content, only to evaluate it all in one go, it seemed pointless considering that no other interaction was needed with the .Content until actual evaluation. Ergo, I switched to replacing the Tags and Constants straight away, instead of storing them and evaluating in a batch. This removed the side effects of the .Evaluate function and helped solve the real main problem - recursion.

I realised that if one tag in a template was replaced immediately with the content of another template, I would need to evaluate the newly present tags too. I was trying to handle this with function calls either inside or out. It would have created a never ending tree of functions, each not doing a lot, with a difficult path to follow on screen, especially if some of those functions were then on shared code-pages. It would be impossible to follow the recursion as it happened.

Instead I only needed to continue using the replace tag function knowing that as each tag was replaced, the new tags were immediately present and ready to be substituted. It also allowed for tags to be shared across all levels of templates by replacing them last. This had removed a huge amount of caller complexity by reducing everything down to just one level and allowing you to template with great complexity in a clean run. It also meant that my class didn’t need to have more bells and whistles to handle a million use cases. I didn’t need more and more tag types to deal with exception cases in the design.

The last part of the design was to make the caller code elegant to look at and as streamlined as possible to avoid having to create and destroy hundreds of variables / class instances and have massive indentations just to do the templating process as this would mix with the logic badly and make things just as hard to follow as before.

By creating a function to both create a new template class, preload the content and then return a self instance allowed me to get a loaded template in one line, without having to Dim any variables! This made the whole thing very self contained and very elegant for ASP.

With LoadTemplate ("EMail/Contact")
	.ReplaceTag "MESSAGE", strMessage
	Site.SendEMail strName, strEMail, strSubject, .Content
End With

I think I managed to achieve the simplicity I desired, at any recursion level, and with the ability to mix dynamic and static content very easily.

In the end I had to ditch any idea of following everybody else’s way of doing things, and the expected norms of programming and start really listening to just myself to hone this code down from a homogeneous blob of confused design and functionality that was trying to cater to too many scenarios instead of defining a solid single scenario to always follow in the first place. Ergo I ended up with a piece of code that would only take a few minutes to write, but took 4 solid days with little sleep, revising and thinking hard to come up with something to suit the 12’500 line project I had to apply it too.