From 0abbefb7739f8beeaf5189758fc037f90ae12c41 Mon Sep 17 00:00:00 2001 From: Johan Lindh Date: Mon, 25 Nov 2024 08:58:48 +0100 Subject: [PATCH] add TemplateReloader --- templatereloader/assets/test.html | 0 templatereloader/templatereloader.go | 61 +++++++++++++++++++++++ templatereloader/templatereloader_test.go | 36 +++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 templatereloader/assets/test.html create mode 100644 templatereloader/templatereloader.go create mode 100644 templatereloader/templatereloader_test.go diff --git a/templatereloader/assets/test.html b/templatereloader/assets/test.html new file mode 100644 index 0000000..e69de29 diff --git a/templatereloader/templatereloader.go b/templatereloader/templatereloader.go new file mode 100644 index 0000000..5c952af --- /dev/null +++ b/templatereloader/templatereloader.go @@ -0,0 +1,61 @@ +package templatereloader + +import ( + "html/template" + "io/fs" + "path" + "time" + + "github.com/linkdata/deadlock" + "github.com/linkdata/jaws" +) + +// A TemplateReloader reloads and reparses templates if more than one second +// has passed since the last Lookup. +type TemplateReloader struct { + Path string // the file path we are loading from + mu deadlock.RWMutex + when time.Time + curr *template.Template +} + +// New returns a jaws.TemplateLookuper. +// +// If deadlock.Debug is false, it calls template.New("").ParseFS(fsys, fpath). +// +// If deadlock.Debug is true, fsys is ignored and it returns a TemplateReloader +// that loads the templates using ParseGlob(relpath/fpath). +func New(fsys fs.FS, fpath, relpath string) (jtl jaws.TemplateLookuper, err error) { + return create(deadlock.Debug, fsys, fpath, relpath) +} + +func create(debug bool, fsys fs.FS, fpath, relpath string) (tl jaws.TemplateLookuper, err error) { + if !debug { + return template.New("").ParseFS(fsys, fpath) + } + var tmpl *template.Template + fpath = path.Join(relpath, fpath) + if tmpl, err = template.New("").ParseGlob(fpath); err == nil { + tl = &TemplateReloader{ + Path: fpath, + when: time.Now(), + curr: tmpl, + } + } + return +} + +func (tr *TemplateReloader) Lookup(name string) *template.Template { + tr.mu.RLock() + tl := tr.curr + d := time.Since(tr.when) + tr.mu.RUnlock() + if d > time.Second { + tr.mu.Lock() + defer tr.mu.Unlock() + tr.curr = template.Must(template.New("").ParseGlob(tr.Path)) + tr.when = tr.when.Add(d) + tl = tr.curr + } + return tl.Lookup(name) +} diff --git a/templatereloader/templatereloader_test.go b/templatereloader/templatereloader_test.go new file mode 100644 index 0000000..e774150 --- /dev/null +++ b/templatereloader/templatereloader_test.go @@ -0,0 +1,36 @@ +package templatereloader + +import ( + "embed" + "testing" + "time" +) + +//go:embed assets +var assetsFS embed.FS + +func TestNew(t *testing.T) { + tl, err := New(assetsFS, "assets/*.html", "") + if err != nil { + t.Fatal(err) + } + + tr := tl.(*TemplateReloader) + tr.when = tr.when.Add(-time.Second * 2) + + tmpl := tl.Lookup("test.html") + if tmpl == nil { + t.Fail() + } +} + +func Test_create_no_debug(t *testing.T) { + tl, err := create(false, assetsFS, "assets/*.html", "") + if err != nil { + t.Fatal(err) + } + tmpl := tl.Lookup("test.html") + if tmpl == nil { + t.Fail() + } +}