Added template renderer service.

This commit is contained in:
mikestefanello 2021-12-19 20:09:01 -05:00
parent d2ce5c34c4
commit 38cf009b70
5 changed files with 146 additions and 78 deletions

View file

@ -2,18 +2,10 @@ package controller
import (
"bytes"
"errors"
"fmt"
"html/template"
"net/http"
"path"
"path/filepath"
"reflect"
"runtime"
"sync"
"goweb/config"
"goweb/funcmap"
"goweb/middleware"
"goweb/msg"
"goweb/services"
@ -27,17 +19,6 @@ import (
"github.com/labstack/echo/v4"
)
var (
// templates stores a cache of parsed page templates
templates = sync.Map{}
// funcMap stores the Template function map
funcMap = funcmap.GetFuncMap()
// templatePath stores the complete path to the templates directory
templatePath = getTemplatesDirectoryPath()
)
// Controller provides base functionality and dependencies to routes.
// The proposed pattern is to embed a Controller in each individual route struct and to use
// the router to inject the container so your routes have access to the services within the container
@ -133,50 +114,20 @@ func (t *Controller) cachePage(c echo.Context, p Page, html *bytes.Buffer) {
// 3. All templates within the components directory
// Also included is the function map provided by the funcmap package
func (t *Controller) parsePageTemplates(p Page) error {
// Check if the template has not yet been parsed or if the app environment is local, so that templates reflect
// changes without having the restart the server
if _, ok := templates.Load(p.Name); !ok || t.Container.Config.App.Environment == config.EnvLocal {
// Parse the Layout and Name templates along with the function map
parsed, err :=
template.New(p.Layout+config.TemplateExt).
Funcs(funcMap).
ParseFiles(
fmt.Sprintf("%s/layouts/%s%s", templatePath, p.Layout, config.TemplateExt),
fmt.Sprintf("%s/pages/%s%s", templatePath, p.Name, config.TemplateExt),
)
if err != nil {
return err
}
// Parse all templates within the components directory
parsed, err = parsed.ParseGlob(fmt.Sprintf("%s/components/*%s", templatePath, config.TemplateExt))
if err != nil {
return err
}
// Store the template so this process only happens once
templates.Store(p.Name, parsed)
}
return nil
return t.Container.Templates.Parse(
"controller",
p.Name,
p.Layout,
[]string{
fmt.Sprintf("layouts/%s", p.Layout),
fmt.Sprintf("pages/%s", p.Name),
},
[]string{"components"})
}
// executeTemplates executes the cached templates belonging to Page and renders the Page within them
func (t *Controller) executeTemplates(p Page) (*bytes.Buffer, error) {
tmpl, ok := templates.Load(p.Name)
if !ok {
return nil, errors.New("uncached page template requested")
}
buf := new(bytes.Buffer)
err := tmpl.(*template.Template).ExecuteTemplate(buf, p.Layout+config.TemplateExt, p)
if err != nil {
return nil, err
}
return buf, nil
return t.Container.Templates.Execute("controller", p.Name, p.Layout, p)
}
// Redirect redirects to a given route name with optional route parameters
@ -227,12 +178,3 @@ func (t *Controller) SetValidationErrorMessages(c echo.Context, err error, data
msg.Danger(c, fmt.Sprintf(message, "<strong>"+label+"</strong>"))
}
}
// getTemplatesDirectoryPath gets the templates directory path
// This is needed incase this is called from a package outside of main,
// such as within tests
func getTemplatesDirectoryPath() string {
_, b, _, _ := runtime.Caller(0)
d := path.Join(path.Dir(b))
return filepath.Join(filepath.Dir(d), config.TemplateDir)
}

View file

@ -2,7 +2,6 @@ package controller
import (
"context"
"html/template"
"io/ioutil"
"net/http"
"net/http/httptest"
@ -128,22 +127,21 @@ func TestController_RenderPage(t *testing.T) {
}
// Check the template cache
parsed, ok := templates.Load(p.Name)
assert.True(t, ok)
parsed, err := c.Templates.Load("controller", p.Name)
assert.NoError(t, err)
// Check that all expected templates were parsed.
// This includes the name, layout and all components
expectedTemplates := make(map[string]bool)
expectedTemplates[p.Name+config.TemplateExt] = true
expectedTemplates[p.Layout+config.TemplateExt] = true
components, err := ioutil.ReadDir(getTemplatesDirectoryPath() + "/components")
components, err := ioutil.ReadDir(c.Templates.GetTemplatesPath() + "/components")
require.NoError(t, err)
for _, f := range components {
expectedTemplates[f.Name()] = true
}
tmpl, ok := parsed.(*template.Template)
require.True(t, ok)
for _, v := range tmpl.Templates() {
for _, v := range parsed.Templates() {
delete(expectedTemplates, v.Name())
}
assert.Empty(t, expectedTemplates)