Merge pull request #962 from gin-gonic/feat/func-maps
Made funMaps for the templates configurable and hot-reloadable
This commit is contained in:
		
							
								
								
									
										1
									
								
								fixtures/basic/raw.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								fixtures/basic/raw.tmpl
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Date: {[{.now | formatAsDate}]} | ||||||
							
								
								
									
										18
									
								
								gin.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								gin.go
									
									
									
									
									
								
							| @ -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. | ||||||
|  | |||||||
							
								
								
									
										54
									
								
								gin_test.go
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								gin_test.go
									
									
									
									
									
								
							| @ -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) { | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -28,6 +28,7 @@ type ( | |||||||
| 		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") | ||||||
| } | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user