// applyBody func applyBody(t *template.Template, names, bodies []string) (*template.Template, error) { for i := 0; i < len(names); i++ { name, body := names[i], bodies[i] var tmpl *template.Template if t == nil { t = template.New(name) } if name == t.Name() { tmpl = t } else { tmpl = t.New(name) } if len(Template.Filters) > 0 { tmpl = applyFilters(tmpl, Template.Filters...) } if Template.Delims.isValid() { tmpl.Delims(Template.Delims.Get()) } DebugPrintf("Parse as \"%s\"\n", name) _, err := tmpl.Parse(body) if err != nil { return nil, err } } return t, nil }
func (session *Session) render(asset string) error { asset = "assets/templates/" + asset var t *template.Template c, ok := session.Config.Cache["template:"+asset] if !ok { log.Trace("Loading asset: %s", asset) a, err := session.Config.AssetLoader(asset) log.Trace("Creating template: %s", asset) t = template.New(asset) t.Delims(session.Config.LeftDelim, session.Config.RightDelim) if err != nil || a == nil { log.Error("Failed loading template %s: %s", asset, err) return err } log.Trace("Parsing template: %s", asset) _, err = t.Parse(string(a)) if err != nil { log.Error("Failed parsing template %s: %s", asset, err) return err } log.Trace("Template parsed successfully: %s", asset) session.Config.Cache["template:"+asset] = t } else { t = c.(*template.Template) log.Trace("Template loaded from cache: %s", asset) } var b bytes.Buffer err := t.Execute(&b, session.Stash) if err != nil { log.Error("Failed executing template %s: %s", asset, err) return err } _, err = session.Response.Write(b.Bytes()) if err != nil { log.Error("Error writing output for template %s: %s", asset, err) return err } return nil }
// parseFiles is the helper for the method and function. If the argument // template is nil, it is created from the first file. func parseFiles(t *template.Template, relativePath string, filenames ...string) (*template.Template, error) { if len(filenames) == 0 { // Not really a problem, but be consistent. return nil, fmt.Errorf("html/template: no files named in call to ParseFiles") } for _, filename := range filenames { b, err := ioutil.ReadFile(filename) if err != nil { return nil, err } s := string(b) name := templateName(relativePath, filename) // First template becomes return value if not already defined, // and we use that one for subsequent New calls to associate // all the templates together. Also, if this file has the same name // as t, this file becomes the contents of t, so // t, err := New(name).Funcs(xxx).ParseFiles(name) // works. Otherwise we create a new template associated with t. var tmpl *template.Template if t == nil { t = template.New(name) } if name == t.Name() { tmpl = t } else { tmpl = t.New(name) } if len(Template.Filters) > 0 { tmpl = applyFilters(tmpl, Template.Filters...) } if Template.Delims.isValid() { tmpl.Delims(Template.Delims.Get()) } DebugPrintf("Parse as \"%s\"\n", name) _, err = tmpl.Parse(s) if err != nil { return nil, err } } return t, nil }
// This scans the views directory and parses all templates as Go Templates. // If a template fails to parse, the error is set on the loader. // (It's awkward to refresh a single Go Template) func (loader *TemplateLoader) Refresh() *Error { TRACE.Printf("Refreshing templates from %s", loader.paths) loader.compileError = nil loader.templatePaths = map[string]string{} // Set the template delimiters for the project if present, then split into left // and right delimiters around a space character var splitDelims []string if TemplateDelims != "" { splitDelims = strings.Split(TemplateDelims, " ") if len(splitDelims) != 2 { log.Fatalln("app.conf: Incorrect format for template.delimiters") } } // Walk through the template loader's paths and build up a template set. var templateSet *template.Template = nil for _, basePath := range loader.paths { // Walk only returns an error if the template loader is completely unusable // (namely, if one of the TemplateFuncs does not have an acceptable signature). // Handling symlinked directories var fullSrcDir string f, err := os.Lstat(basePath) if err == nil && f.Mode()&os.ModeSymlink == os.ModeSymlink { fullSrcDir, err = filepath.EvalSymlinks(basePath) if err != nil { panic(err) } } else { fullSrcDir = basePath } var templateWalker func(path string, info os.FileInfo, err error) error templateWalker = func(path string, info os.FileInfo, err error) error { if err != nil { ERROR.Println("error walking templates:", err) return nil } // is it a symlinked template? link, err := os.Lstat(path) if err == nil && link.Mode()&os.ModeSymlink == os.ModeSymlink { TRACE.Println("symlink template:", path) // lookup the actual target & check for goodness targetPath, err := filepath.EvalSymlinks(path) if err != nil { ERROR.Println("Failed to read symlink", err) return err } targetInfo, err := os.Stat(targetPath) if err != nil { ERROR.Println("Failed to stat symlink target", err) return err } // set the template path to the target of the symlink path = targetPath info = targetInfo // need to save state and restore for recursive call to Walk on symlink tmp := fullSrcDir fullSrcDir = filepath.Dir(targetPath) filepath.Walk(targetPath, templateWalker) fullSrcDir = tmp } // Walk into watchable directories if info.IsDir() { if !loader.WatchDir(info) { return filepath.SkipDir } return nil } // Only add watchable if !loader.WatchFile(info.Name()) { return nil } var fileStr string // addTemplate loads a template file into the Go template loader so it can be rendered later addTemplate := func(templateName string) (err error) { TRACE.Println("adding template: ", templateName) // Convert template names to use forward slashes, even on Windows. if os.PathSeparator == '\\' { templateName = strings.Replace(templateName, `\`, `/`, -1) // ` } // If we already loaded a template of this name, skip it. if _, ok := loader.templatePaths[templateName]; ok { return nil } loader.templatePaths[templateName] = path // Load the file if we haven't already if fileStr == "" { fileBytes, err := ioutil.ReadFile(path) if err != nil { ERROR.Println("Failed reading file:", path) return nil } fileStr = string(fileBytes) } if templateSet == nil { // Create the template set. This panics if any of the funcs do not // conform to expectations, so we wrap it in a func and handle those // panics by serving an error page. var funcError *Error func() { defer func() { if err := recover(); err != nil { funcError = &Error{ Title: "Panic (Template Loader)", Description: fmt.Sprintln(err), } } }() templateSet = template.New(templateName).Funcs(TemplateFuncs) // If alternate delimiters set for the project, change them for this set if splitDelims != nil && basePath == ViewsPath { templateSet.Delims(splitDelims[0], splitDelims[1]) } else { // Reset to default otherwise templateSet.Delims("", "") } _, err = templateSet.Parse(fileStr) }() if funcError != nil { return funcError } } else { if splitDelims != nil && basePath == ViewsPath { templateSet.Delims(splitDelims[0], splitDelims[1]) } else { templateSet.Delims("", "") } _, err = templateSet.New(templateName).Parse(fileStr) } return err } templateName := path[len(fullSrcDir)+1:] err = addTemplate(templateName) // Store / report the first error encountered. if err != nil && loader.compileError == nil { _, line, description := parseTemplateError(err) loader.compileError = &Error{ Title: "Template Compilation Error", Path: templateName, Description: description, Line: line, SourceLines: strings.Split(fileStr, "\n"), } ERROR.Printf("Template compilation error (In %s around line %d):\n%s", templateName, line, description) } return nil } funcErr := filepath.Walk(fullSrcDir, templateWalker) // If there was an error with the Funcs, set it and return immediately. if funcErr != nil { loader.compileError = funcErr.(*Error) return loader.compileError } } // Note: compileError may or may not be set. loader.templateSet = templateSet return loader.compileError }
// This scans the views directory and parses all templates as Go Templates. // If a template fails to parse, the error is set on the loader. // (It's awkward to refresh a single Go Template) func (loader *TemplateLoader) Refresh() *Error { TRACE.Printf("Refreshing templates from %s", loader.paths) loader.compileError = nil loader.templatePaths = map[string]string{} // Set the template delimiters for the project if present, then split into left // and right delimiters around a space character var splitDelims []string if TemplateDelims != "" { splitDelims = strings.Split(TemplateDelims, " ") if len(splitDelims) != 2 { log.Fatalln("app.conf: Incorrect format for template.delimiters") } } // Walk through the template loader's paths and build up a template set. var templateSet *template.Template = nil for _, basePath := range loader.paths { // Walk only returns an error if the template loader is completely unusable // (namely, if one of the TemplateFuncs does not have an acceptable signature). funcErr := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error { if err != nil { ERROR.Println("error walking templates:", err) return nil } // Walk into watchable directories if info.IsDir() { if !loader.WatchDir(info) { return filepath.SkipDir } return nil } // Only add watchable if !loader.WatchFile(info.Name()) { return nil } // Convert template names to use forward slashes, even on Windows. // Lower case the file name for case-insensitive matching templateName := strings.ToLower(path[len(basePath)+1:]) if os.PathSeparator == '\\' { templateName = strings.Replace(templateName, `\`, `/`, -1) // ` } // If we already loaded a template of this name, skip it. if _, ok := loader.templatePaths[templateName]; ok { return nil } loader.templatePaths[templateName] = path fileBytes, err := ioutil.ReadFile(path) if err != nil { ERROR.Println("Failed reading file:", path) return nil } fileStr := string(fileBytes) if templateSet == nil { // Create the template set. This panics if any of the funcs do not // conform to expectations, so we wrap it in a func and handle those // panics by serving an error page. var funcError *Error func() { defer func() { if err := recover(); err != nil { funcError = &Error{ Title: "Panic (Template Loader)", Description: fmt.Sprintln(err), } } }() templateSet = template.New(templateName).Funcs(TemplateFuncs) // If alternate delimiters set for the project, change them for this set if splitDelims != nil && basePath == ViewsPath { templateSet.Delims(splitDelims[0], splitDelims[1]) } else { // Reset to default otherwise templateSet.Delims("", "") } _, err = templateSet.Parse(fileStr) }() if funcError != nil { return funcError } } else { if splitDelims != nil && basePath == ViewsPath { templateSet.Delims(splitDelims[0], splitDelims[1]) } else { templateSet.Delims("", "") } _, err = templateSet.New(templateName).Parse(fileStr) } // Store / report the first error encountered. if err != nil && loader.compileError == nil { _, line, description := parseTemplateError(err) loader.compileError = &Error{ Title: "Template Compilation Error", Path: templateName, Description: description, Line: line, SourceLines: strings.Split(fileStr, "\n"), } ERROR.Printf("Template compilation error (In %s around line %d):\n%s", templateName, line, description) } return nil }) // If there was an error with the Funcs, set it and return immediately. if funcErr != nil { loader.compileError = funcErr.(*Error) return loader.compileError } } // Note: compileError may or may not be set. loader.templateSet = templateSet return loader.compileError }
// CompileResultWithTemplate compiles the parsed result and associates it with t. func CompileResultWithTemplate(t *template.Template, rslt *result, opts *Options) (*template.Template, error) { // Initialize the options. opts = initializeOptions(opts) var err error // Create a buffer. baseBf := bytes.NewBuffer(nil) innerBf := bytes.NewBuffer(nil) includeBfs := make(map[string]*bytes.Buffer) // Write data to the buffer. for _, e := range rslt.base { if _, err := e.WriteTo(baseBf); err != nil { return nil, err } } for _, e := range rslt.inner { if _, err = e.WriteTo(innerBf); err != nil { return nil, err } } for path, elements := range rslt.includes { bf := bytes.NewBuffer(nil) // Write a define action. bf.WriteString(fmt.Sprintf(actionDefine, opts.DelimLeft, path, opts.DelimRight)) for _, e := range elements { if _, err = e.WriteTo(bf); err != nil { return nil, err } } // Write an end action. bf.WriteString(fmt.Sprintf(actionEnd, opts.DelimLeft, opts.DelimRight)) includeBfs[path] = bf } // Set Delimiters. t.Delims(opts.DelimLeft, opts.DelimRight) // Set FuncMaps. t.Funcs(template.FuncMap{ preDefinedFuncNameHTML: func(s string) template.HTML { return template.HTML(s) }, }) t.Funcs(opts.FuncMap) // Parse a string to the template. t, err = t.Parse(baseBf.String()) if err != nil { return nil, err } t, err = t.Parse(innerBf.String()) if err != nil { return nil, err } for _, bf := range includeBfs { t, err = t.Parse(bf.String()) if err != nil { return nil, err } } return t, nil }