Last active
February 11, 2024 05:17
-
-
Save nicolasparada/7e46682d8f99f5944adc380f5232de37 to your computer and use it in GitHub Desktop.
renderer.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package renderer | |
import ( | |
"fmt" | |
"html/template" | |
"io" | |
"io/fs" | |
"path/filepath" | |
"sync" | |
) | |
type Renderer struct { | |
FS fs.FS | |
BaseDir string | |
IncludePatters []string | |
mu sync.Mutex | |
templates map[string]*template.Template | |
} | |
func New(fs fs.FS, baseDir string, includePatterns ...string) *Renderer { | |
return &Renderer{ | |
FS: fs, | |
BaseDir: baseDir, | |
IncludePatters: includePatterns, | |
templates: map[string]*template.Template{}, | |
} | |
} | |
func (r *Renderer) Render(w io.Writer, name string, data any) error { | |
tmpl, ok := r.template(name) | |
if !ok { | |
tmpl, err := template.New(name).Funcs(funcMap).ParseFS(r.FS, r.patterns(name)...) | |
if err != nil { | |
return err | |
} | |
r.setTemplate(name, tmpl) | |
return tmpl.Execute(w, data) | |
} | |
return tmpl.Execute(w, data) | |
} | |
func (r *Renderer) patterns(name string) []string { | |
out := make([]string, len(r.IncludePatters)+1) | |
for i, p := range r.IncludePatters { | |
out[i] = filepath.Join(r.BaseDir, p) | |
} | |
out[len(out)-1] = filepath.Join(r.BaseDir, name) | |
return out | |
} | |
func (r *Renderer) template(name string) (*template.Template, bool) { | |
r.mu.Lock() | |
defer r.mu.Unlock() | |
t, ok := r.templates[name] | |
return t, ok | |
} | |
func (r *Renderer) setTemplate(name string, t *template.Template) { | |
r.mu.Lock() | |
defer r.mu.Unlock() | |
r.templates[name] = t | |
} | |
var funcMap = template.FuncMap{ | |
"dict": func(keyvals ...any) (map[string]any, error) { | |
if len(keyvals)%2 != 0 { | |
return nil, fmt.Errorf("odd number of keyvals") | |
} | |
out := map[string]any{} | |
for i := 0; i < len(keyvals); i += 2 { | |
k, ok := keyvals[i].(string) | |
if !ok { | |
return nil, fmt.Errorf("key not a string: %T", keyvals[i]) | |
} | |
out[k] = keyvals[i+1] | |
} | |
return out, nil | |
}, | |
"list": func(elems ...any) []any { | |
return elems | |
}, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package web | |
import ( | |
"embed" | |
"net/http" | |
"sync" | |
"acme.example/renderer" | |
) | |
//go:embed templates/includes/*.tmpl templates/*.tmpl | |
var templatesFS embed.FS | |
type Handler struct { | |
renderer *renderer.Renderer | |
once sync.Once | |
handler http.Handler | |
} | |
func (h *Handler) init() { | |
h.renderer = renderer.New(templatesFS, "templates", "includes/*.tmpl") | |
mux := http.NewServeMux() | |
mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { | |
h.renderer.Render(w, "welcome.tmpl", nil) | |
}) | |
h.handler = mux | |
} | |
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
h.once.Do(h.init) | |
h.handler.ServeHTTP(w, r) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment