From 17b69ea3a31b07b9aadd1b02c137fe38861516ce Mon Sep 17 00:00:00 2001 From: Pavel Date: Thu, 28 Nov 2019 11:32:49 +0300 Subject: [PATCH 1/2] avoid panic if template was added twice --- core/template.go | 46 +++++++++++++++++++++++++++++++++++++------ core/template_test.go | 28 +++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/core/template.go b/core/template.go index 3732fbe..986ed39 100644 --- a/core/template.go +++ b/core/template.go @@ -11,23 +11,29 @@ import ( type Renderer struct { multitemplate.Renderer TemplatesBox *packr.Box - FuncMap template.FuncMap + FuncMap template.FuncMap + alreadyAdded map[string]*template.Template } // NewRenderer is a Renderer constructor func NewRenderer(funcMap template.FuncMap) Renderer { return Renderer{ - Renderer: multitemplate.NewRenderer(), - FuncMap: funcMap, + Renderer: multitemplate.NewRenderer(), + FuncMap: funcMap, + alreadyAdded: map[string]*template.Template{}, } } // Push is an AddFromFilesFuncs wrapper func (r *Renderer) Push(name string, files ...string) *template.Template { + if tpl := r.getTemplate(name); tpl != nil { + return tpl + } + if r.TemplatesBox == nil { - return r.AddFromFilesFuncs(name, r.FuncMap, files...) + return r.storeTemplate(name, r.AddFromFilesFuncs(name, r.FuncMap, files...)) } else { - return r.addFromBox(name, r.FuncMap, files...) + return r.storeTemplate(name, r.addFromBox(name, r.FuncMap, files...)) } } @@ -42,4 +48,32 @@ func (r *Renderer) addFromBox(name string, funcMap template.FuncMap, files ...st } return r.AddFromStringsFuncs(name, funcMap, filesData...) -} \ No newline at end of file +} + +// storeTemplate stores built template if multitemplate.DynamicRender is used. +// Dynamic render doesn't store templates - it stores builders, that's why we can't just extract them. +// It possibly can cause data inconsistency in developer enviroments where return value from Renderer.Push is used. +func (r *Renderer) storeTemplate(name string, tpl *template.Template) *template.Template { + if _, ok := r.Renderer.(multitemplate.DynamicRender); ok { + r.alreadyAdded[name] = tpl + } + + return tpl +} + +// getTemplate returns template from render or from storage +func (r *Renderer) getTemplate(name string) *template.Template { + if renderer, ok := r.Renderer.(multitemplate.Render); ok { + if i, ok := renderer[name]; ok { + return i + } + } + + if _, ok := r.Renderer.(multitemplate.DynamicRender); ok { + if i, ok := r.alreadyAdded[name]; ok { + return i + } + } + + return nil +} diff --git a/core/template_test.go b/core/template_test.go index c32dec7..c89fd7c 100644 --- a/core/template_test.go +++ b/core/template_test.go @@ -24,6 +24,11 @@ type TemplateTest struct { } func (t *TemplateTest) SetupSuite() { + t.initTestData() + t.renderer = t.initRenderer() +} + +func (t *TemplateTest) initTestData() { if _, err := os.Stat(testTemplatesDir); err != nil && os.IsNotExist(err) { err := os.Mkdir(testTemplatesDir, os.ModePerm) require.Nil(t.T(), err) @@ -34,8 +39,10 @@ func (t *TemplateTest) SetupSuite() { require.Nil(t.T(), err1) require.Nil(t.T(), err2) } +} - t.renderer = NewRenderer(template.FuncMap{ +func (t *TemplateTest) initRenderer() Renderer { + return NewRenderer(template.FuncMap{ "trans": func(data string) string { if data == "test" { return "ok" @@ -51,6 +58,25 @@ func (t *TemplateTest) Test_Push() { assert.Equal(t.T(), 3, len(tpl.Templates())) } +func (t *TemplateTest) Test_PushAlreadyExists() { + defer func() { + assert.Nil(t.T(), recover()) + }() + + tpl := t.renderer.Push("index", fmt.Sprintf(testTemplatesFile, 1), fmt.Sprintf(testTemplatesFile, 2)) + assert.Equal(t.T(), 3, len(tpl.Templates())) +} + +func (t *TemplateTest) Test_PushNewInstance() { + defer func() { + assert.Nil(t.T(), recover()) + }() + + newInstance := t.initRenderer() + tpl := newInstance.Push("index", fmt.Sprintf(testTemplatesFile, 1), fmt.Sprintf(testTemplatesFile, 2)) + assert.Equal(t.T(), 3, len(tpl.Templates())) +} + func TestTemplate_Suite(t *testing.T) { suite.Run(t, new(TemplateTest)) } From 6cab6169b15863a5387d7aa979645522026f72f2 Mon Sep 17 00:00:00 2001 From: Pavel Date: Thu, 28 Nov 2019 12:13:58 +0300 Subject: [PATCH 2/2] better tests, ability to create only dynamic or only static renderer --- core/template.go | 17 ++++++++++- core/template_test.go | 66 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 72 insertions(+), 11 deletions(-) diff --git a/core/template.go b/core/template.go index 986ed39..6e43619 100644 --- a/core/template.go +++ b/core/template.go @@ -17,8 +17,23 @@ type Renderer struct { // NewRenderer is a Renderer constructor func NewRenderer(funcMap template.FuncMap) Renderer { + return newRendererWithMultitemplate(funcMap, multitemplate.NewRenderer()) +} + +// NewStaticRenderer is a Renderer constructor with multitemplate.Render +func NewStaticRenderer(funcMap template.FuncMap) Renderer { + return newRendererWithMultitemplate(funcMap, multitemplate.New()) +} + +// NewStaticRenderer is a Renderer constructor with multitemplate.DynamicRender +func NewDynamicRenderer(funcMap template.FuncMap) Renderer { + return newRendererWithMultitemplate(funcMap, multitemplate.NewDynamic()) +} + +// newRendererWithMultitemplate initializes Renderer with provided multitemplate.Renderer instance +func newRendererWithMultitemplate(funcMap template.FuncMap, renderer multitemplate.Renderer) Renderer { return Renderer{ - Renderer: multitemplate.NewRenderer(), + Renderer: renderer, FuncMap: funcMap, alreadyAdded: map[string]*template.Template{}, } diff --git a/core/template_test.go b/core/template_test.go index c89fd7c..21ab439 100644 --- a/core/template_test.go +++ b/core/template_test.go @@ -8,6 +8,7 @@ import ( "path" "testing" + "github.com/gin-contrib/multitemplate" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -20,12 +21,14 @@ var ( type TemplateTest struct { suite.Suite - renderer Renderer + static Renderer + dynamic Renderer } func (t *TemplateTest) SetupSuite() { t.initTestData() - t.renderer = t.initRenderer() + t.static = t.initStatic() + t.dynamic = t.initDynamic() } func (t *TemplateTest) initTestData() { @@ -41,8 +44,20 @@ func (t *TemplateTest) initTestData() { } } -func (t *TemplateTest) initRenderer() Renderer { - return NewRenderer(template.FuncMap{ +func (t *TemplateTest) initStatic() Renderer { + return NewStaticRenderer(template.FuncMap{ + "trans": func(data string) string { + if data == "test" { + return "ok" + } + + return "fail" + }, + }) +} + +func (t *TemplateTest) initDynamic() Renderer { + return NewDynamicRenderer(template.FuncMap{ "trans": func(data string) string { if data == "test" { return "ok" @@ -54,8 +69,10 @@ func (t *TemplateTest) initRenderer() Renderer { } func (t *TemplateTest) Test_Push() { - tpl := t.renderer.Push("index", fmt.Sprintf(testTemplatesFile, 1), fmt.Sprintf(testTemplatesFile, 2)) - assert.Equal(t.T(), 3, len(tpl.Templates())) + tplStatic := t.static.Push("index", fmt.Sprintf(testTemplatesFile, 1), fmt.Sprintf(testTemplatesFile, 2)) + tplDynamic := t.dynamic.Push("index", fmt.Sprintf(testTemplatesFile, 1), fmt.Sprintf(testTemplatesFile, 2)) + assert.Equal(t.T(), 3, len(tplStatic.Templates())) + assert.Equal(t.T(), 3, len(tplDynamic.Templates())) } func (t *TemplateTest) Test_PushAlreadyExists() { @@ -63,20 +80,49 @@ func (t *TemplateTest) Test_PushAlreadyExists() { assert.Nil(t.T(), recover()) }() - tpl := t.renderer.Push("index", fmt.Sprintf(testTemplatesFile, 1), fmt.Sprintf(testTemplatesFile, 2)) - assert.Equal(t.T(), 3, len(tpl.Templates())) + tplStatic := t.static.Push("index", fmt.Sprintf(testTemplatesFile, 1), fmt.Sprintf(testTemplatesFile, 2)) + tplDynamic := t.dynamic.Push("index", fmt.Sprintf(testTemplatesFile, 1), fmt.Sprintf(testTemplatesFile, 2)) + assert.Equal(t.T(), 3, len(tplStatic.Templates())) + assert.Equal(t.T(), 3, len(tplDynamic.Templates())) } -func (t *TemplateTest) Test_PushNewInstance() { +func (t *TemplateTest) Test_PushNewInstanceStatic() { defer func() { assert.Nil(t.T(), recover()) }() - newInstance := t.initRenderer() + newInstance := t.initStatic() tpl := newInstance.Push("index", fmt.Sprintf(testTemplatesFile, 1), fmt.Sprintf(testTemplatesFile, 2)) assert.Equal(t.T(), 3, len(tpl.Templates())) } +func (t *TemplateTest) Test_PushNewInstanceDynamic() { + defer func() { + assert.Nil(t.T(), recover()) + }() + + newInstance := t.initDynamic() + tpl := newInstance.Push("index", fmt.Sprintf(testTemplatesFile, 1), fmt.Sprintf(testTemplatesFile, 2)) + assert.Equal(t.T(), 3, len(tpl.Templates())) +} + +func TestTemplate_NewRenderer(t *testing.T) { + r := NewRenderer(template.FuncMap{}) + assert.NotNil(t, r) +} + +func TestTemplate_NewStaticRenderer(t *testing.T) { + r := NewStaticRenderer(template.FuncMap{}) + assert.NotNil(t, r) + assert.IsType(t, multitemplate.New(), r.Renderer) +} + +func TestTemplate_NewDynamicRenderer(t *testing.T) { + r := NewDynamicRenderer(template.FuncMap{}) + assert.NotNil(t, r) + assert.IsType(t, multitemplate.NewDynamic(), r.Renderer) +} + func TestTemplate_Suite(t *testing.T) { suite.Run(t, new(TemplateTest)) }