package html import ( "fmt" "html/template" "io" "io/ioutil" "net/http" "path/filepath" "github.com/oxtoacart/bpool" ) // TemplateService is a template/html based templating service type TemplateService struct { templates map[string]*template.Template pool *bpool.BufferPool helpers template.FuncMap } func (t *TemplateService) LoadLayout(name string, layout string, blocks []string) error { tmpl := template.New(name) tmpl.Funcs(t.helpers) for _, b := range blocks { if _, err := tmpl.Parse(b); err != nil { return err } } tmpl, err := tmpl.Parse(layout) if err != nil { return err } t.templates[name] = tmpl return nil } // LoadTemplatesDir loads the templates used by the service func (t *TemplateService) LoadTemplatesDir(templatesDir string) error { layoutFiles, err := filepath.Glob(filepath.Join(templatesDir, "layouts", "*.tmpl")) if err != nil { return err } blockFiles, err := filepath.Glob(filepath.Join(templatesDir, "blocks", "*.tmpl")) if err != nil { return err } blockTemplates := make([]string, 0, len(blockFiles)) for _, f := range blockFiles { templateData, err := ioutil.ReadFile(f) if err != nil { return err } blockTemplates = append(blockTemplates, string(templateData)) } // Generate our templates map from our layouts/ and blocks/ directories for _, f := range layoutFiles { templateData, err := ioutil.ReadFile(f) if err != nil { return err } templateName := filepath.Base(f) if err := t.LoadLayout(templateName, string(templateData), blockTemplates); err != nil { return err } } return nil } // RenderPage renders a template to the given http.ResponseWriter func (t *TemplateService) RenderPage(w http.ResponseWriter, templateName string, data interface{}) error { // Ensure the template exists in the map. tmpl, ok := t.templates[templateName] if !ok { return fmt.Errorf("the template '%s' does not exist", templateName) } // Create a buffer to temporarily write to and check if any errors were encountered. buf := t.pool.Get() defer t.pool.Put(buf) if err := tmpl.ExecuteTemplate(buf, templateName, data); err != nil { return err } // Set the header and write the buffer to the http.ResponseWriter w.Header().Set("Content-Type", "text/html; charset=utf-8") _, err := buf.WriteTo(w) return err } // Render renders a template to the given io.Writer func (t *TemplateService) Render(w io.Writer, templateName string, data interface{}) error { // Ensure the template exists in the map. tmpl, ok := t.templates[templateName] if !ok { return fmt.Errorf("the template '%s' does not exist", templateName) } // Create a buffer to temporarily write to and check if any errors were encountered. buf := t.pool.Get() defer t.pool.Put(buf) if err := tmpl.ExecuteTemplate(buf, templateName, data); err != nil { return err } _, err := buf.WriteTo(w) return err } // NewTemplateService returns a new Service func NewTemplateService(funcs ...OptionFunc) *TemplateService { options := defaultOptions() for _, f := range funcs { f(options) } return &TemplateService{ templates: make(map[string]*template.Template), pool: bpool.NewBufferPool(options.PoolSize), helpers: options.Helpers, } }