Skip to content

Commit

Permalink
update html template renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
xgfone committed Apr 27, 2020
1 parent 50b58b6 commit 6b95076
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 53 deletions.
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,11 +402,7 @@ import (
func main() {
// It will recursively load all the files in the directory as the templates.
loader := template.NewDirLoader("/path/to/templates")
tmplRender, err := template.NewHTMLRender(loader, false)
if err != nil {
fmt.Println(err)
return
}
tmplRender := template.NewHTMLTemplateRender(loader)

router := ship.Default()
router.Renderer.(*render.MuxRenderer).Add(".tmpl", tmplRender)
Expand Down
109 changes: 68 additions & 41 deletions render/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ import (
"path/filepath"
"strings"
"sync"

"github.com/xgfone/ship/v2/render"
)

// File represents a template file.
Expand Down Expand Up @@ -142,58 +140,77 @@ func (l tmplLoader) loadFile(prefix, filename string) (File, error) {
return NewFile(name, ext, data), nil
}

// NewHTMLRender returns a new Renderer to render the html template.
//
// If debug is true, it will reload all the templates automatically
// each time the template is rendered.
//
// The returned Renderer has a method `Reload() error` to reload
// all the templates, and you can use as follow:
//
// htmlRender, _ := NewHTMLRender(loader, false)
// err := htmlRender.(interface{ Reload() error }).Reload()
// // ...
//
func NewHTMLRender(loader Loader, debug bool) (render.Renderer, error) {
return newHTMLRender(loader, debug, false)
// NewHTMLTemplateRender returns a new Renderer to render the html template.
func NewHTMLTemplateRender(loader Loader) *HTMLTemplateRender {
r := &HTMLTemplateRender{
loader: loader,
bufs: sync.Pool{New: func() interface{} { return new(bytes.Buffer) }},
}
return r
}

// NewSafeHTMLRender is the same as NewHTMLRender, but you can reload
// all the templates safely and concurrently.
func NewSafeHTMLRender(loader Loader, debug bool) (render.Renderer, error) {
return newHTMLRender(loader, debug, true)
// HTMLTemplateRender is used to render a html/template.
type HTMLTemplateRender struct {
loader Loader
funcs []template.FuncMap
debug bool
right string
left string

load sync.Once
lock *sync.RWMutex
tmpl *template.Template
bufs sync.Pool
}

func newHTMLRender(loader Loader, debug, safe bool) (render.Renderer, error) {
r := &htmlRender{
loader: loader,
bufs: sync.Pool{New: func() interface{} { return new(bytes.Buffer) }},
}
if safe {
// Debug sets the debug model and returns itself.
//
// If debug is true, it will reload all the templates automatically each time
// the template is rendered.
func (r *HTMLTemplateRender) Debug(debug bool) *HTMLTemplateRender {
r.debug = debug
return r
}

// Lock enables the lock to reload the templates safely and concurrently,
// then returns itself.
//
// Notice: There is no need to enable the lock when no reloading the templates
// during rendering the templates.
func (r *HTMLTemplateRender) Lock(lock bool) *HTMLTemplateRender {
if lock && r.lock == nil {
r.lock = new(sync.RWMutex)
} else if !lock && r.lock != nil {
r.lock = nil
}
return r
}

if err := r.reload(); err != nil {
return nil, err
}
return r, nil
// Delims resets the left and right delimiter and returns itself.
//
// The default delimiters are "{{" and "}}".
//
// Notice: it must be set before rendering the html template.
func (r *HTMLTemplateRender) Delims(left, right string) *HTMLTemplateRender {
r.left = left
r.right = right
return r
}

// HTMLRender is used to render a html/template.
type htmlRender struct {
loader Loader
debug bool
lock *sync.RWMutex
tmpl *template.Template
bufs sync.Pool
// Funcs appends the FuncMap and returns itself.
//
// Notice: it must be set before rendering the html template.
func (r *HTMLTemplateRender) Funcs(funcs template.FuncMap) *HTMLTemplateRender {
r.funcs = append(r.funcs, funcs)
return r
}

// Reload reloads all the templates.
func (r *htmlRender) Reload() error {
func (r *HTMLTemplateRender) Reload() error {
return r.reload()
}

func (r *htmlRender) reload() error {
func (r *HTMLTemplateRender) reload() error {
files, err := r.loader.LoadAll()
if err != nil {
return err
Expand All @@ -205,8 +222,12 @@ func (r *htmlRender) reload() error {
}

tmpl := template.New("__DEFAULT_HTML_TEMPLATE__")
tmpl.Delims(r.left, r.right)
for _, file := range files {
t := tmpl.New(file.Name())
for _, funcs := range r.funcs {
t.Funcs(funcs)
}
if _, err = t.Parse(string(file.Data())); err != nil {
return err
}
Expand All @@ -215,20 +236,26 @@ func (r *htmlRender) reload() error {
return nil
}

func (r *htmlRender) execute(w io.Writer, name string, data interface{}) error {
func (r *HTMLTemplateRender) execute(w io.Writer, name string, data interface{}) error {
if r.lock != nil {
r.lock.RLock()
defer r.lock.RUnlock()
}
return r.tmpl.ExecuteTemplate(w, name, data)
}

func (r *htmlRender) Render(w http.ResponseWriter, name string, code int,
// Render implements the interface render.Renderer.
func (r *HTMLTemplateRender) Render(w http.ResponseWriter, name string, code int,
data interface{}) (err error) {
if r.debug {
if err = r.reload(); err != nil {
return
}
} else {
r.load.Do(func() { err = r.reload() })
if err != nil {
return
}
}

buf := r.bufs.Get().(*bytes.Buffer)
Expand Down
9 changes: 2 additions & 7 deletions render/template/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ var htmpresp = `<!DOCTYPE html>
</html>
`

func TestNewHTMLRender(t *testing.T) {
func TestNewHTMLTemplateRender(t *testing.T) {
err := ioutil.WriteFile(tmplname, []byte(htmlTmpl), 0600)
if err != nil {
t.Error(err)
Expand Down Expand Up @@ -94,12 +94,7 @@ func TestNewHTMLRender(t *testing.T) {
}
}

r, err := NewHTMLRender(loader, false)
if err != nil {
t.Error(err)
return
}

r := NewHTMLTemplateRender(loader)
rec := httptest.NewRecorder()
err = r.Render(rec, tmplname, 200, "This is the content.")
if err != nil {
Expand Down

0 comments on commit 6b95076

Please sign in to comment.