Skip to content

Commit cb7a867

Browse files
committed
templates: add WithFileReadOnRender option
1 parent 6d32702 commit cb7a867

File tree

1 file changed

+59
-30
lines changed

1 file changed

+59
-30
lines changed

templates/templates.go

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,16 @@ var ErrUnknownTemplate = fmt.Errorf("unknown template")
4040

4141
// Options holds parameters for creating Templates.
4242
type Options struct {
43-
fileFindFunc func(filename string) string
44-
fileReadFunc FileReadFunc
45-
contentType string
46-
files map[string][]string
47-
strings map[string][]string
48-
functions template.FuncMap
49-
delimOpen string
50-
delimClose string
51-
logf func(format string, a ...interface{})
43+
fileFindFunc func(filename string) string
44+
fileReadFunc FileReadFunc
45+
fileReadOnRender bool
46+
contentType string
47+
files map[string][]string
48+
strings map[string][]string
49+
functions template.FuncMap
50+
delimOpen string
51+
delimClose string
52+
logf func(format string, a ...interface{})
5253
}
5354

5455
// Option sets parameters used in New function.
@@ -83,6 +84,15 @@ func WithFileReadFunc(fn FileReadFunc) Option {
8384
return func(o *Options) { o.fileReadFunc = fn }
8485
}
8586

87+
// WithFileReadOnRender forces template files to be read and
88+
// parsed every time Render or Respond functions are called.
89+
// This is useful for quickly reloading template files,
90+
// but with a performance cost. This functionality
91+
// is disabled by default.
92+
func WithFileReadOnRender(yes bool) Option {
93+
return func(o *Options) { o.fileReadOnRender = yes }
94+
}
95+
8696
// WithTemplateFromFiles adds a template parsed from files.
8797
func WithTemplateFromFiles(name string, files ...string) Option {
8898
return func(o *Options) { o.files[name] = files }
@@ -142,6 +152,7 @@ func WithLogFunc(logf func(format string, a ...interface{})) Option {
142152
// Templates structure holds parsed templates.
143153
type Templates struct {
144154
templates map[string]*template.Template
155+
parseFiles func(name string) (*template.Template, error)
145156
defaultName string
146157
contentType string
147158
logf func(format string, a ...interface{})
@@ -181,16 +192,31 @@ func New(opts ...Option) (t *Templates, err error) {
181192
}
182193
t.templates[name] = tpl
183194
}
184-
for name, files := range o.files {
195+
196+
parse := func(files []string) (tpl *template.Template, err error) {
185197
fs := []string{}
186198
for _, f := range files {
187199
fs = append(fs, o.fileFindFunc(f))
188200
}
189-
tpl, err := parseFiles(o.fileReadFunc, template.New("").Funcs(o.functions).Delims(o.delimOpen, o.delimClose), fs...)
190-
if err != nil {
191-
return nil, err
201+
return parseFiles(o.fileReadFunc, template.New("").Funcs(o.functions).Delims(o.delimOpen, o.delimClose), fs...)
202+
}
203+
204+
if o.fileReadOnRender {
205+
t.parseFiles = func(name string) (tpl *template.Template, err error) {
206+
files, ok := o.files[name]
207+
if !ok {
208+
return nil, &Error{Err: ErrUnknownTemplate, Template: name}
209+
}
210+
return parse(files)
211+
}
212+
} else {
213+
for name, files := range o.files {
214+
tpl, err := parse(files)
215+
if err != nil {
216+
return nil, err
217+
}
218+
t.templates[name] = tpl
192219
}
193-
t.templates[name] = tpl
194220
}
195221
return
196222
}
@@ -199,11 +225,8 @@ func New(opts ...Option) (t *Templates, err error) {
199225
// then writes the the status and body to the response writer.
200226
// A panic will be raised if the template does not exist or fails to execute.
201227
func (t Templates) RespondTemplateWithStatus(w http.ResponseWriter, name, templateName string, data interface{}, status int) {
228+
tpl := t.mustTemplate(name)
202229
buf := bytes.Buffer{}
203-
tpl, ok := t.templates[name]
204-
if !ok {
205-
panic(&Error{Err: ErrUnknownTemplate, Template: name})
206-
}
207230
if err := tpl.ExecuteTemplate(&buf, templateName, data); err != nil {
208231
panic(err)
209232
}
@@ -222,11 +245,8 @@ func (t Templates) RespondTemplateWithStatus(w http.ResponseWriter, name, templa
222245
// then writes the the status and body to the response writer.
223246
// A panic will be raised if the template does not exist or fails to execute.
224247
func (t Templates) RespondWithStatus(w http.ResponseWriter, name string, data interface{}, status int) {
248+
tpl := t.mustTemplate(name)
225249
buf := bytes.Buffer{}
226-
tpl, ok := t.templates[name]
227-
if !ok {
228-
panic(&Error{Err: ErrUnknownTemplate, Template: name})
229-
}
230250
if err := tpl.Execute(&buf, data); err != nil {
231251
panic(err)
232252
}
@@ -257,11 +277,8 @@ func (t Templates) Respond(w http.ResponseWriter, name string, data interface{})
257277

258278
// RenderTemplate executes a named template and returns the string.
259279
func (t Templates) RenderTemplate(name, templateName string, data interface{}) (s string, err error) {
280+
tpl := t.mustTemplate(name)
260281
buf := bytes.Buffer{}
261-
tpl, ok := t.templates[name]
262-
if !ok {
263-
return "", &Error{Err: ErrUnknownTemplate, Template: name}
264-
}
265282
if err := tpl.ExecuteTemplate(&buf, templateName, data); err != nil {
266283
return "", err
267284
}
@@ -270,17 +287,29 @@ func (t Templates) RenderTemplate(name, templateName string, data interface{}) (
270287

271288
// Render executes a template and returns the string.
272289
func (t Templates) Render(name string, data interface{}) (s string, err error) {
290+
tpl := t.mustTemplate(name)
273291
buf := bytes.Buffer{}
274-
tpl, ok := t.templates[name]
275-
if !ok {
276-
return "", &Error{Err: ErrUnknownTemplate, Template: name}
277-
}
278292
if err := tpl.Execute(&buf, data); err != nil {
279293
return "", err
280294
}
281295
return buf.String(), nil
282296
}
283297

298+
func (t Templates) mustTemplate(name string) (tpl *template.Template) {
299+
tpl, ok := t.templates[name]
300+
if ok {
301+
return tpl
302+
}
303+
if t.parseFiles != nil {
304+
tpl, err := t.parseFiles(name)
305+
if err != nil {
306+
panic(err)
307+
}
308+
return tpl
309+
}
310+
panic(&Error{Err: ErrUnknownTemplate, Template: name})
311+
}
312+
284313
func parseFiles(fn FileReadFunc, t *template.Template, filenames ...string) (*template.Template, error) {
285314
for _, filename := range filenames {
286315
b, err := fn(filename)

0 commit comments

Comments
 (0)