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,
}
}