package revel import ( "bufio" "bytes" "errors" "fmt" "path/filepath" "strings" ) type TemplateEngine interface { // prase template string and add template to the set. ParseAndAdd(basePath *TemplateView) error // returns Template corresponding to the given templateName, or nil Lookup(templateName string) Template // Fired by the template loader when events occur Event(event Event, arg interface{}) // returns true if this engine should be used to parse the file specified in baseTemplate Handles(templateView *TemplateView) bool // returns the name of the engine Name() string } // The template view information type TemplateView struct { TemplateName string // The name of the view FilePath string // The file path (view relative) BasePath string // The file system base path FileBytes []byte // The file loaded EngineType string // The name of the engine used to render the view } var templateLoaderMap = map[string]func(loader *TemplateLoader) (TemplateEngine, error){} // Allow for templates to be registered during init but not initialized until application has been started func RegisterTemplateLoader(key string, loader func(loader *TemplateLoader) (TemplateEngine, error)) (err error) { if _, found := templateLoaderMap[key]; found { err = fmt.Errorf("Template loader %s already exists", key) } templateLog.Debug("Registered template engine loaded", "name", key) templateLoaderMap[key] = loader return } // Sets the template name from Config // Sets the template API methods for parsing and storing templates before rendering func (loader *TemplateLoader) CreateTemplateEngine(templateEngineName string) (TemplateEngine, error) { if "" == templateEngineName { templateEngineName = GO_TEMPLATE } factory := templateLoaderMap[templateEngineName] if nil == factory { fmt.Printf("registered factories %#v\n %s \n", templateLoaderMap, templateEngineName) return nil, errors.New("Unknown template engine name - " + templateEngineName + ".") } templateEngine, err := factory(loader) if nil != err { return nil, errors.New("Failed to init template engine (" + templateEngineName + "), " + err.Error()) } templateLog.Debug("CreateTemplateEngine: init templates", "name", templateEngineName) return templateEngine, nil } // Passing in a comma delimited list of engine names to be used with this loader to parse the template files func (loader *TemplateLoader) initializeEngines(runtimeLoader *templateRuntime, templateEngineNameList string) (err *Error) { // Walk through the template loader's paths and build up a template set. if templateEngineNameList == "" { templateEngineNameList = GO_TEMPLATE } runtimeLoader.templatesAndEngineList = []TemplateEngine{} for _, engine := range strings.Split(templateEngineNameList, ",") { engine := strings.TrimSpace(strings.ToLower(engine)) if templateLoader, err := loader.CreateTemplateEngine(engine); err != nil { runtimeLoader.compileError = &Error{ Title: "Panic (Template Loader)", Description: err.Error(), } return runtimeLoader.compileError } else { // Always assign a default engine, switch it if it is specified in the config runtimeLoader.templatesAndEngineList = append(runtimeLoader.templatesAndEngineList, templateLoader) } } return } func EngineHandles(engine TemplateEngine, templateView *TemplateView) bool { if line, _, e := bufio.NewReader(bytes.NewBuffer(templateView.FileBytes)).ReadLine(); e == nil && string(line[:3]) == "#! " { // Extract the shebang and look at the rest of the line // #! pong2 // #! go templateType := strings.TrimSpace(string(line[2:])) if engine.Name() == templateType { // Advance the read file bytes so it does not include the shebang templateView.FileBytes = templateView.FileBytes[len(line)+1:] templateView.EngineType = templateType return true } } filename := filepath.Base(templateView.FilePath) bits := strings.Split(filename, ".") if len(bits) > 2 { templateType := strings.TrimSpace(bits[len(bits)-2]) if engine.Name() == templateType { templateView.EngineType = templateType return true } } return false }