~netlandish/gobwebs

810628aa70a03ba71b3f2adba188ed9256e28229 — Peter Sanchez a month ago 5820feb
Adding proper template rendering instead of full template mashup
2 files changed, 88 insertions(+), 15 deletions(-)

M server/server.go
M validate/template.go
M server/server.go => server/server.go +25 -0
@@ 129,6 129,31 @@ func (c *Context) Render(code int, name string, data interface{}) (err error) {
	return c.HTMLBlob(code, buf.Bytes())
}

// RenderTemplate is like Render except it accepts a *template.Template instance
// to render from.
func (c *Context) RenderTemplate(
	tmpl *template.Template, code int, name string, data interface{}) (err error) {
	if c.Server.e.Renderer == nil {
		return echo.ErrRendererNotRegistered
	}
	vtmpl := c.Server.e.Renderer.(*validate.Template)
	buf := new(bytes.Buffer)
	if err = vtmpl.RenderTemplate(tmpl, buf, name, data, c); err != nil {
		return
	}
	return c.HTMLBlob(code, buf.Bytes())
}

// NewTemplate will return a new template.Template instance based on the
// current templateBase (see validate.Template)
func (c *Context) NewTemplate(tfs fs.FS, filenames ...string) (*template.Template, error) {
	if c.Server.e.Renderer == nil {
		return nil, echo.ErrRendererNotRegistered
	}
	vtmpl := c.Server.e.Renderer.(*validate.Template)
	return vtmpl.New(tfs, filenames...)
}

// Error is needed because otherwise it will send an instance of
// *echo.context, and we want the user info, etc. from the gobwebs
// Context struct

M validate/template.go => validate/template.go +63 -15
@@ 9,9 9,6 @@ import (
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"netlandish.com/x/gobwebs"

	// Postgres
	_ "github.com/lib/pq"
)

type (


@@ 24,16 21,47 @@ type (

	// Template defines the template registry struct
	Template struct {
		templates *template.Template
		templateBase *template.Template
		templates    *template.Template

		staticMap   Map
		staticFuncs []func(c echo.Context) Map
		cloned      bool
	}
)

// NewTemplate returns a *Template instance
func NewTemplate() *Template {
	return &Template{templates: template.New(""), staticMap: make(Map)}
	return &Template{
		templateBase: template.New(""),
		templates:    template.New(""),
		staticMap:    make(Map),
	}
}

// New will return a clone of `t.templateBase` having
// already parsed the given files. This is used when you don't want to use
// the larger mix of already processed templates.
func (t *Template) New(tfs fs.FS, filenames ...string) (*template.Template, error) {
	tmpl, err := t.CloneBase()
	if err != nil {
		return nil, err
	}
	if tfs != nil {
		tmpl, err = tmpl.ParseFS(tfs, filenames...)
	} else {
		tmpl, err = tmpl.ParseFiles(filenames...)
	}
	if err != nil {
		return nil, err
	}
	return tmpl, nil
}

// Clone will clone the `t.templateBase` Template to ensure all added static
// funcs and variables are available
func (t *Template) CloneBase() (*template.Template, error) {
	return t.templateBase.Clone()
}

// Templates returns local *template.Template object


@@ 74,42 102,62 @@ func (t *Template) tmplPayload(c echo.Context, data Map) (Map, error) {
	return input, nil
}

// Render implements e.Renderer interface
func (t *Template) Render(w io.Writer, name string, data any, c echo.Context) error {
func (t *Template) render(
	tmpl *template.Template, w io.Writer, name string, data any, c echo.Context) error {
	// Add global methods if data is a map. Should be reached 99% of time
	if viewContext, ok := data.(Map); ok {
		input, err := t.tmplPayload(c, viewContext)
		if err != nil {
			return err
		}
		return t.templates.ExecuteTemplate(w, name, input)
		return tmpl.ExecuteTemplate(w, name, input)
	}
	return t.templates.ExecuteTemplate(w, name, data)
	return tmpl.ExecuteTemplate(w, name, data)
}

// Render implements e.Renderer interface
func (t *Template) Render(w io.Writer, name string, data any, c echo.Context) error {
	return t.render(t.templates, w, name, data, c)
}

// RenderTemplate takes a *template.Template object and populates it's
// context with the static values already provided by `t`.
func (t *Template) RenderTemplate(
	tmpl *template.Template, w io.Writer, name string, data any, c echo.Context) error {
	return t.render(tmpl, w, name, data, c)
}

// LoadTemplates parses templates given the glob pattern path
func (t *Template) LoadTemplates(path string, tfs fs.FS) error {
	// XXX Why do we have to use the tmp var here?
	var (
		tmpt *template.Template
		err  error
		tmpl *template.Template
	)

	if !t.cloned {
		vtmpl, err := t.CloneBase()
		if err != nil {
			return err
		}
		t.templates = vtmpl
		t.cloned = true
	}

	if tfs != nil {
		tmpt, err = t.templates.ParseFS(tfs, path)
		tmpl, err = t.templates.ParseFS(tfs, path)
	} else {
		tmpt, err = t.templates.ParseGlob(path)
		tmpl, err = t.templates.ParseGlob(path)
	}
	if err != nil {
		return err
	}
	t.templates = tmpt
	t.templates = tmpl
	return nil
}

// AddFuncs adds template functions to the rendering template
func (t *Template) AddFuncs(funcs template.FuncMap) {
	t.templates = t.templates.Funcs(funcs)
	t.templateBase.Funcs(funcs)
}

// AddStaticFunc adds a function to be called when building the default