package revel
import (
"html/template"
"io"
"log"
"strings"
)
const GO_TEMPLATE = "go"
// Called on startup, initialized when the REVEL_BEFORE_MODULES_LOADED is called
func init() {
AddInitEventHandler(func(typeOf Event, value interface{}) (responseOf EventResponse) {
if typeOf == REVEL_BEFORE_MODULES_LOADED {
RegisterTemplateLoader(GO_TEMPLATE, func(loader *TemplateLoader) (TemplateEngine, error) {
// Set the template delimiters for the project if present, then split into left
// and right delimiters around a space character
TemplateDelims := Config.StringDefault("template.go.delimiters", "")
var splitDelims []string
if TemplateDelims != "" {
splitDelims = strings.Split(TemplateDelims, " ")
if len(splitDelims) != 2 {
log.Fatalln("app.conf: Incorrect format for template.delimiters")
}
}
return &GoEngine{
loader: loader,
templateSet: template.New("__root__").Funcs(TemplateFuncs),
templatesByName: map[string]*GoTemplate{},
splitDelims: splitDelims,
}, nil
})
}
return
})
}
// Adapter for Go Templates.
type GoTemplate struct {
*template.Template
engine *GoEngine
*TemplateView
}
// return a 'revel.Template' from Go's template.
func (gotmpl GoTemplate) Render(wr io.Writer, arg interface{}) error {
return gotmpl.Execute(wr, arg)
}
// The main template engine for Go
type GoEngine struct {
// The template loader
loader *TemplateLoader
// THe current template set
templateSet *template.Template
// A map of templates by name
templatesByName map[string]*GoTemplate
// The delimiter that is used to indicate template code, defaults to {{
splitDelims []string
// True if map is case insensitive
CaseInsensitive bool
}
// Convert the path to lower case if needed
func (i *GoEngine) ConvertPath(path string) string {
if i.CaseInsensitive {
return strings.ToLower(path)
}
return path
}
// Returns true if this engine can handle the response
func (i *GoEngine) Handles(templateView *TemplateView) bool {
return EngineHandles(i, templateView)
}
// Parses the template vide and adds it to the template set
func (engine *GoEngine) ParseAndAdd(baseTemplate *TemplateView) error {
// If alternate delimiters set for the project, change them for this set
if engine.splitDelims != nil && strings.Index(baseTemplate.Location(), ViewsPath) > -1 {
engine.templateSet.Delims(engine.splitDelims[0], engine.splitDelims[1])
} else {
// Reset to default otherwise
engine.templateSet.Delims("", "")
}
templateSource := string(baseTemplate.FileBytes)
templateName := engine.ConvertPath(baseTemplate.TemplateName)
tpl, err := engine.templateSet.New(baseTemplate.TemplateName).Parse(templateSource)
if nil != err {
_, line, description := ParseTemplateError(err)
return &Error{
Title: "Template Compilation Error",
Path: baseTemplate.TemplateName,
Description: description,
Line: line,
SourceLines: strings.Split(templateSource, "\n"),
}
}
engine.templatesByName[templateName] = &GoTemplate{Template: tpl, engine: engine, TemplateView: baseTemplate}
return nil
}
// Lookups the template name, to see if it is contained in this engine
func (engine *GoEngine) Lookup(templateName string) Template {
// Case-insensitive matching of template file name
if tpl, found := engine.templatesByName[engine.ConvertPath(templateName)]; found {
return tpl
}
return nil
}
// Return the engine name
func (engine *GoEngine) Name() string {
return GO_TEMPLATE
}
// An event listener to listen for Revel INIT events
func (engine *GoEngine) Event(action Event, i interface{}) {
if action == TEMPLATE_REFRESH_REQUESTED {
// At this point all the templates have been passed into the
engine.templatesByName = map[string]*GoTemplate{}
engine.templateSet = template.New("__root__").Funcs(TemplateFuncs)
// Check to see what should be used for case sensitivity
engine.CaseInsensitive = Config.BoolDefault("go.template.caseinsensitive", true)
}
}