From 810628aa70a03ba71b3f2adba188ed9256e28229 Mon Sep 17 00:00:00 2001 From: Peter Sanchez Date: Mon, 4 Mar 2024 18:32:49 -0600 Subject: [PATCH] Adding proper template rendering instead of full template mashup --- server/server.go | 25 ++++++++++++++ validate/template.go | 78 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 88 insertions(+), 15 deletions(-) diff --git a/server/server.go b/server/server.go index ae88a3f..f0f82e5 100644 --- a/server/server.go +++ b/server/server.go @@ -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 diff --git a/validate/template.go b/validate/template.go index 60cd80a..22cbcaa 100644 --- a/validate/template.go +++ b/validate/template.go @@ -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 -- 2.45.2