Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package tpl
- import (
- "io"
- "log"
- "path/filepath"
- "strings"
- "text/template"
- "time"
- "github.com/fsnotify/fsnotify"
- "github.com/labstack/echo"
- )
- // Renderer is a custom html/template renderer for Echo framework
- type Renderer struct {
- templates *template.Template
- whatchDir string
- tempateExt string
- }
- // New constructs a new instance of TplRenderer
- func New() *Renderer {
- return &Renderer{}
- }
- // Render renders a template document
- func (r *Renderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
- // Add global methods if data is a map
- if viewContext, isMap := data.(map[string]interface{}); isMap {
- viewContext["reverse"] = c.Echo().Reverse
- }
- // Hot reload doesn't work correctly with base template
- //if r.templates.Lookup("base"+r.tempateExt) != nil {
- // t := r.templates.Lookup(name)
- // return t.ExecuteTemplate(w, "base", data)
- //}
- return r.templates.ExecuteTemplate(w, name, data)
- }
- // LoadTemplates finds and loads templates by specified pattern
- func (r *Renderer) LoadTemplates(pattern string) {
- r.whatchDir = filepath.Dir(pattern) + "/"
- r.tempateExt = filepath.Ext(pattern)
- if r.whatchDir == ""{
- log.Fatal("Template directory not found!")
- }
- if r.tempateExt == "" {
- log.Fatal("Template extension can't be recognized!")
- }
- log.Print("Directory to watch: ", r.whatchDir)
- log.Print("Template file extension: ", r.tempateExt)
- r.templates = template.Must(template.ParseGlob(pattern))
- }
- // StartFSWatcher reloads templates on their modification.
- // This method should be executed only after LoadTemplates method
- // as it uses watchDir and templateExt fields
- // which extracted from template pattern.
- // Otherwise it terminates an app with log.Fatal()
- func (r *Renderer) StartFSWatcher() {
- if r.templates == nil {
- log.Fatal("StartFSWatcher method should be executed only after LoadTempates method")
- }
- watcher, err := fsnotify.NewWatcher()
- if err != nil {
- log.Fatal("Failed to create FS watcher: ", err)
- }
- go func() {
- // used as intermediate storage for updated templates
- updatedTemplates := map[string]bool{}
- tick := time.NewTicker(time.Millisecond * 500)
- for {
- select {
- case <-tick.C:
- templates := make([]string, 0)
- for tp, isUpdated := range updatedTemplates {
- if isUpdated {
- updatedTemplates[tp] = false
- templates = append(templates, tp)
- }
- }
- if len(templates) > 0 {
- r.updateTemplates(templates...)
- }
- case event, ok := <-watcher.Events:
- if !ok {
- return
- }
- // as jetbrains IDE uses "safe write" it must also catch RENAME
- if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Rename == fsnotify.Rename {
- // filters template files by extension
- if strings.HasSuffix(event.Name, r.tempateExt) {
- updatedTemplates[event.Name] = true
- }
- }
- case err, ok := <-watcher.Errors:
- if !ok {
- return
- }
- log.Print("Template directory watcher returned error: ", err)
- }
- }
- }()
- err = watcher.Add(r.whatchDir)
- if err != nil {
- log.Fatal(err)
- }
- }
- // updateTemplates is a helper called by
- // a StartWatcher endless loop to reload updated templates
- func (r *Renderer) updateTemplates(tpl ...string) {
- log.Print("Template has been reloaded:", tpl)
- r.templates = template.Must(r.templates.ParseFiles(tpl...))
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement