robfig/revel — pull request #506 by jivko-chobanov (2014-02-15 14:25:00+00:00)
Title: Pluggable template loader

# Pluggable template loader
Provided an API to make adapters in the form template_adapter_xxx.go consisting of #initialAddAndParse, #addAndParse, #lookup on abstractTemplateSet and also #Name, #Render, #Content on each template kind. Extracted html/template from template.go. Created adapter for gohaml.

# Sorry for the guideline mistake
I did not fork from develop but - from master. Some tests (checking if the given address appears on the page rendered) do not pass, though the booking demo front page looks correct.

# TODO:
* HAML adapter's #Render method needs to do some conversions to arg in order to transmit data from application to the template.
* Also gohaml does not provide template funcs like "url" as html/template does.
* Some tests (checking if the given address appears on the page rendered) do not pass, though the booking demo front page looks correct. As I am not so into revel, I could not fix that easily and left it for somebody with more experience.

# Features:
To plug a new template engine you need to:
1. Create an adapter for xxx engine in /template_adapter_xxx.go containing
```
var TemplateAPIOfXxx map[string]interface{} {
"initialAddAndParse": lambda-implementation,
"addAndParse": lambda-implementation,
"lookup": lambda-implementation,
}
```
- see templateAndEnvironment type in template.go for further explaination.
2. In the adapter file create a struct implementing the `Template` interface - #Name, #Content, #Render, to contain the parsed teplates
3. Add 2 lines in #setTemplateEngineMethods in template.go that add your implementation to `templatesAndEngine.methods`.
4. In app.conf set `template.engine=xxx` to actually use that template engine

# Design decisions
I used map[string]interface{} for storing the API implementation instead of methods with same names on different receivers (template engines), because with the map I need only one place to check the exact type of the engine (see step 3. in Features above). Otherwise before each API call I would have to cast the `abstractTemplateSet` to the specific type in order to call the right implementation or the other option is to realize the API with different suffices on method names which is the same.