Merge pull request #962 from gin-gonic/feat/func-maps

Made funMaps for the templates configurable and hot-reloadable
This commit is contained in:
Javier Provecho Fernandez 2017-07-02 10:51:52 +02:00 committed by GitHub
commit 54fe9df889
4 changed files with 75 additions and 12 deletions

1
fixtures/basic/raw.tmpl Normal file
View File

@ -0,0 +1 @@
Date: {[{.now | formatAsDate}]}

18
gin.go
View File

@ -47,6 +47,7 @@ type (
RouterGroup RouterGroup
delims render.Delims delims render.Delims
HTMLRender render.HTMLRender HTMLRender render.HTMLRender
FuncMap template.FuncMap
allNoRoute HandlersChain allNoRoute HandlersChain
allNoMethod HandlersChain allNoMethod HandlersChain
noRoute HandlersChain noRoute HandlersChain
@ -112,6 +113,7 @@ func New() *Engine {
basePath: "/", basePath: "/",
root: true, root: true,
}, },
FuncMap: template.FuncMap{},
RedirectTrailingSlash: true, RedirectTrailingSlash: true,
RedirectFixedPath: false, RedirectFixedPath: false,
HandleMethodNotAllowed: false, HandleMethodNotAllowed: false,
@ -147,19 +149,19 @@ func (engine *Engine) Delims(left, right string) *Engine {
func (engine *Engine) LoadHTMLGlob(pattern string) { func (engine *Engine) LoadHTMLGlob(pattern string) {
if IsDebugging() { if IsDebugging() {
debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).ParseGlob(pattern))) debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern)))
engine.HTMLRender = render.HTMLDebug{Glob: pattern, Delims: engine.delims} engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
} else { } else {
templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).ParseGlob(pattern)) templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern))
engine.SetHTMLTemplate(templ) engine.SetHTMLTemplate(templ)
} }
} }
func (engine *Engine) LoadHTMLFiles(files ...string) { func (engine *Engine) LoadHTMLFiles(files ...string) {
if IsDebugging() { if IsDebugging() {
engine.HTMLRender = render.HTMLDebug{Files: files, Delims: engine.delims} engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
} else { } else {
templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).ParseFiles(files...)) templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
engine.SetHTMLTemplate(templ) engine.SetHTMLTemplate(templ)
} }
} }
@ -169,7 +171,11 @@ func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
debugPrintWARNINGSetHTMLTemplate() debugPrintWARNINGSetHTMLTemplate()
} }
engine.HTMLRender = render.HTMLProduction{Template: templ} engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
}
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
engine.FuncMap = funcMap
} }
// NoRoute adds handlers for NoRoute. It return a 404 code by default. // NoRoute adds handlers for NoRoute. It return a 404 code by default.

View File

@ -6,6 +6,7 @@ package gin
import ( import (
"fmt" "fmt"
"html/template"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"reflect" "reflect"
@ -15,14 +16,28 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func formatAsDate(t time.Time) string {
year, month, day := t.Date()
return fmt.Sprintf("%d/%02d/%02d", year, month, day)
}
func setupHTMLFiles(t *testing.T) func() { func setupHTMLFiles(t *testing.T) func() {
go func() { go func() {
SetMode(TestMode)
router := New() router := New()
router.Delims("{[{", "}]}") router.Delims("{[{", "}]}")
router.LoadHTMLFiles("./fixtures/basic/hello.tmpl") router.SetFuncMap(template.FuncMap{
"formatAsDate": formatAsDate,
})
router.LoadHTMLFiles("./fixtures/basic/hello.tmpl", "./fixtures/basic/raw.tmpl")
router.GET("/test", func(c *Context) { router.GET("/test", func(c *Context) {
c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"}) c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"})
}) })
router.GET("/raw", func(c *Context) {
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
})
})
router.Run(":8888") router.Run(":8888")
}() }()
t.Log("waiting 1 second for server startup") t.Log("waiting 1 second for server startup")
@ -32,12 +47,21 @@ func setupHTMLFiles(t *testing.T) func() {
func setupHTMLGlob(t *testing.T) func() { func setupHTMLGlob(t *testing.T) func() {
go func() { go func() {
SetMode(DebugMode)
router := New() router := New()
router.Delims("{[{", "}]}") router.Delims("{[{", "}]}")
router.SetFuncMap(template.FuncMap{
"formatAsDate": formatAsDate,
})
router.LoadHTMLGlob("./fixtures/basic/*") router.LoadHTMLGlob("./fixtures/basic/*")
router.GET("/test", func(c *Context) { router.GET("/test", func(c *Context) {
c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"}) c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"})
}) })
router.GET("/raw", func(c *Context) {
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
})
})
router.Run(":8888") router.Run(":8888")
}() }()
t.Log("waiting 1 second for server startup") t.Log("waiting 1 second for server startup")
@ -59,6 +83,20 @@ func TestLoadHTMLGlob(t *testing.T) {
td() td()
} }
func TestLoadHTMLGlobFromFuncMap(t *testing.T) {
time.Now()
td := setupHTMLGlob(t)
res, err := http.Get("http://127.0.0.1:8888/raw")
if err != nil {
fmt.Println(err)
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "Date: 2017/07/01\n", string(resp[:]))
td()
}
// func (engine *Engine) LoadHTMLFiles(files ...string) { // func (engine *Engine) LoadHTMLFiles(files ...string) {
// func (engine *Engine) RunTLS(addr string, cert string, key string) error { // func (engine *Engine) RunTLS(addr string, cert string, key string) error {
@ -100,6 +138,20 @@ func TestLoadHTMLFiles(t *testing.T) {
td() td()
} }
func TestLoadHTMLFilesFuncMap(t *testing.T) {
time.Now()
td := setupHTMLFiles(t)
res, err := http.Get("http://127.0.0.1:8888/raw")
if err != nil {
fmt.Println(err)
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "Date: 2017/07/01\n", string(resp[:]))
td()
}
func TestLoadHTMLReleaseMode(t *testing.T) { func TestLoadHTMLReleaseMode(t *testing.T) {
} }

View File

@ -25,9 +25,10 @@ type (
} }
HTMLDebug struct { HTMLDebug struct {
Files []string Files []string
Glob string Glob string
Delims Delims Delims Delims
FuncMap template.FuncMap
} }
HTML struct { HTML struct {
@ -55,11 +56,14 @@ func (r HTMLDebug) Instance(name string, data interface{}) Render {
} }
} }
func (r HTMLDebug) loadTemplate() *template.Template { func (r HTMLDebug) loadTemplate() *template.Template {
if r.FuncMap == nil {
r.FuncMap = template.FuncMap{}
}
if len(r.Files) > 0 { if len(r.Files) > 0 {
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).ParseFiles(r.Files...)) return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFiles(r.Files...))
} }
if len(r.Glob) > 0 { if len(r.Glob) > 0 {
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).ParseGlob(r.Glob)) return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob))
} }
panic("the HTML debug render was created without files or glob pattern") panic("the HTML debug render was created without files or glob pattern")
} }