all: Refactor to nonglobal template handling

Updates #2701
This commit is contained in:
Bjørn Erik Pedersen 2017-01-10 01:36:59 +01:00 committed by GitHub
parent 4ea4359ac1
commit d6000a208c
17 changed files with 255 additions and 209 deletions

View file

@ -19,16 +19,17 @@ import (
"html/template" "html/template"
"net/url" "net/url"
"os" "os"
"path/filepath"
"regexp" "regexp"
"strings" "strings"
"testing" "testing"
"io/ioutil" "io/ioutil"
"log" "log"
"path/filepath"
"github.com/spf13/hugo/tpl"
"github.com/spf13/hugo/helpers" "github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/tpl"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -106,9 +107,8 @@ void do();
"(?s)^\n<div class=\"highlight\" style=\"background: #f0f0f0\"><pre style=\"line-height: 125%\">.*?void</span>.*?do</span>.*?().*?</pre></div>\n$", "(?s)^\n<div class=\"highlight\" style=\"background: #f0f0f0\"><pre style=\"line-height: 125%\">.*?void</span>.*?do</span>.*?().*?</pre></div>\n$",
}, },
} { } {
templ := tpl.New(logger)
p, _ := pageFromString(simplePage, "simple.md") p, _ := pageFromString(simplePage, "simple.md")
output, err := HandleShortcodes(this.in, p, templ) output, err := HandleShortcodes(this.in, p)
if err != nil { if err != nil {
t.Fatalf("[%d] Handle shortcode error", i) t.Fatalf("[%d] Handle shortcode error", i)
@ -150,9 +150,8 @@ func TestShortcodeFigure(t *testing.T) {
"(?s)^\n<figure >.*?<img src=\"/img/hugo-logo.png\" />.*?<figcaption>.*?<p>.*?<a href=\"/img/hugo-logo.png\">.*?Hugo logo.*?</a>.*?</p>.*?</figcaption>.*?</figure>\n$", "(?s)^\n<figure >.*?<img src=\"/img/hugo-logo.png\" />.*?<figcaption>.*?<p>.*?<a href=\"/img/hugo-logo.png\">.*?Hugo logo.*?</a>.*?</p>.*?</figcaption>.*?</figure>\n$",
}, },
} { } {
templ := tpl.New(logger)
p, _ := pageFromString(simplePage, "simple.md") p, _ := pageFromString(simplePage, "simple.md")
output, err := HandleShortcodes(this.in, p, templ) output, err := HandleShortcodes(this.in, p)
matched, err := regexp.MatchString(this.expected, output) matched, err := regexp.MatchString(this.expected, output)
@ -175,9 +174,8 @@ func TestShortcodeSpeakerdeck(t *testing.T) {
"(?s)^<script async class='speakerdeck-embed' data-id='4e8126e72d853c0060001f97'.*?>.*?</script>$", "(?s)^<script async class='speakerdeck-embed' data-id='4e8126e72d853c0060001f97'.*?>.*?</script>$",
}, },
} { } {
templ := tpl.New(logger)
p, _ := pageFromString(simplePage, "simple.md") p, _ := pageFromString(simplePage, "simple.md")
output, err := HandleShortcodes(this.in, p, templ) output, err := HandleShortcodes(this.in, p)
matched, err := regexp.MatchString(this.expected, output) matched, err := regexp.MatchString(this.expected, output)
@ -210,9 +208,8 @@ func TestShortcodeYoutube(t *testing.T) {
"(?s)^\n<div class=\"video\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\\?autoplay=1\".*?allowfullscreen frameborder=\"0\">.*?</iframe>.*?</div>$", "(?s)^\n<div class=\"video\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\\?autoplay=1\".*?allowfullscreen frameborder=\"0\">.*?</iframe>.*?</div>$",
}, },
} { } {
templ := tpl.New(logger)
p, _ := pageFromString(simplePage, "simple.md") p, _ := pageFromString(simplePage, "simple.md")
output, err := HandleShortcodes(this.in, p, templ) output, err := HandleShortcodes(this.in, p)
matched, err := regexp.MatchString(this.expected, output) matched, err := regexp.MatchString(this.expected, output)
@ -245,9 +242,8 @@ func TestShortcodeVimeo(t *testing.T) {
"(?s)^<div class=\"video\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>$", "(?s)^<div class=\"video\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>$",
}, },
} { } {
templ := tpl.New(logger)
p, _ := pageFromString(simplePage, "simple.md") p, _ := pageFromString(simplePage, "simple.md")
output, err := HandleShortcodes(this.in, p, templ) output, err := HandleShortcodes(this.in, p)
matched, err := regexp.MatchString(this.expected, output) matched, err := regexp.MatchString(this.expected, output)
@ -274,9 +270,8 @@ func TestShortcodeGist(t *testing.T) {
"(?s)^<script src=\"//gist.github.com/spf13/7896402.js\\?file=img.html\"></script>$", "(?s)^<script src=\"//gist.github.com/spf13/7896402.js\\?file=img.html\"></script>$",
}, },
} { } {
templ := tpl.New(logger)
p, _ := pageFromString(simplePage, "simple.md") p, _ := pageFromString(simplePage, "simple.md")
output, err := HandleShortcodes(this.in, p, templ) output, err := HandleShortcodes(this.in, p)
matched, err := regexp.MatchString(this.expected, output) matched, err := regexp.MatchString(this.expected, output)
@ -313,13 +308,14 @@ func TestShortcodeTweet(t *testing.T) {
}, },
} }
templ := tpl.New(logger) p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
templ.Lookup("").Funcs(tweetFuncMap) templ.Funcs(tweetFuncMap)
return nil
})
p, _ := pageFromString(simplePage, "simple.md")
cacheFileID := viper.GetString("cacheDir") + url.QueryEscape("https://api.twitter.com/1/statuses/oembed.json?id=666616452582129664") cacheFileID := viper.GetString("cacheDir") + url.QueryEscape("https://api.twitter.com/1/statuses/oembed.json?id=666616452582129664")
defer os.Remove(cacheFileID) defer os.Remove(cacheFileID)
output, err := HandleShortcodes(this.in, p, templ) output, err := HandleShortcodes(this.in, p)
matched, err := regexp.MatchString(this.expected, output) matched, err := regexp.MatchString(this.expected, output)
@ -353,7 +349,7 @@ func TestShortcodeInstagram(t *testing.T) {
}, },
} { } {
// overload getJSON to return mock API response from Instagram // overload getJSON to return mock API response from Instagram
tweetFuncMap := template.FuncMap{ instagramFuncMap := template.FuncMap{
"getJSON": func(urlParts ...string) interface{} { "getJSON": func(urlParts ...string) interface{} {
var v interface{} var v interface{}
err := json.Unmarshal([]byte(this.resp), &v) err := json.Unmarshal([]byte(this.resp), &v)
@ -365,13 +361,14 @@ func TestShortcodeInstagram(t *testing.T) {
}, },
} }
templ := tpl.New(logger) p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
templ.Lookup("").Funcs(tweetFuncMap) templ.Funcs(instagramFuncMap)
return nil
})
p, _ := pageFromString(simplePage, "simple.md")
cacheFileID := viper.GetString("cacheDir") + url.QueryEscape("https://api.instagram.com/oembed/?url=https://instagram.com/p/BMokmydjG-M/&hidecaption="+this.hidecaption) cacheFileID := viper.GetString("cacheDir") + url.QueryEscape("https://api.instagram.com/oembed/?url=https://instagram.com/p/BMokmydjG-M/&hidecaption="+this.hidecaption)
defer os.Remove(cacheFileID) defer os.Remove(cacheFileID)
output, err := HandleShortcodes(this.in, p, templ) output, err := HandleShortcodes(this.in, p)
if err != nil { if err != nil {
t.Fatalf("[%d] Failed to render shortcodes", i) t.Fatalf("[%d] Failed to render shortcodes", i)

View file

@ -15,12 +15,11 @@ package hugolib
import ( import (
"github.com/spf13/hugo/source" "github.com/spf13/hugo/source"
"github.com/spf13/hugo/tpl"
) )
type Handler interface { type Handler interface {
FileConvert(*source.File, *Site) HandledResult FileConvert(*source.File, *Site) HandledResult
PageConvert(*Page, tpl.Template) HandledResult PageConvert(*Page) HandledResult
Read(*source.File, *Site) HandledResult Read(*source.File, *Site) HandledResult
Extensions() []string Extensions() []string
} }

View file

@ -18,7 +18,6 @@ import (
"github.com/dchest/cssmin" "github.com/dchest/cssmin"
"github.com/spf13/hugo/source" "github.com/spf13/hugo/source"
"github.com/spf13/hugo/tpl"
) )
func init() { func init() {
@ -32,7 +31,7 @@ func (h basicFileHandler) Read(f *source.File, s *Site) HandledResult {
return HandledResult{file: f} return HandledResult{file: f}
} }
func (h basicFileHandler) PageConvert(*Page, tpl.Template) HandledResult { func (h basicFileHandler) PageConvert(*Page) HandledResult {
return HandledResult{} return HandledResult{}
} }

View file

@ -74,7 +74,7 @@ func (mh *MetaHandle) Convert(i interface{}, s *Site, results HandleResults) {
return return
} }
results <- h.PageConvert(p, s.owner.tmpl) results <- h.PageConvert(p)
} }
} }

View file

@ -19,7 +19,6 @@ import (
"github.com/spf13/hugo/helpers" "github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/source" "github.com/spf13/hugo/source"
"github.com/spf13/hugo/tpl"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -56,8 +55,8 @@ type markdownHandler struct {
} }
func (h markdownHandler) Extensions() []string { return []string{"mdown", "markdown", "md"} } func (h markdownHandler) Extensions() []string { return []string{"mdown", "markdown", "md"} }
func (h markdownHandler) PageConvert(p *Page, t tpl.Template) HandledResult { func (h markdownHandler) PageConvert(p *Page) HandledResult {
return commonConvert(p, t) return commonConvert(p)
} }
type htmlHandler struct { type htmlHandler struct {
@ -65,7 +64,9 @@ type htmlHandler struct {
} }
func (h htmlHandler) Extensions() []string { return []string{"html", "htm"} } func (h htmlHandler) Extensions() []string { return []string{"html", "htm"} }
func (h htmlHandler) PageConvert(p *Page, t tpl.Template) HandledResult {
// TODO(bep) globals use p.s.t
func (h htmlHandler) PageConvert(p *Page) HandledResult {
if p.rendered { if p.rendered {
panic(fmt.Sprintf("Page %q already rendered, does not need conversion", p.BaseFileName())) panic(fmt.Sprintf("Page %q already rendered, does not need conversion", p.BaseFileName()))
} }
@ -73,7 +74,7 @@ func (h htmlHandler) PageConvert(p *Page, t tpl.Template) HandledResult {
// Work on a copy of the raw content from now on. // Work on a copy of the raw content from now on.
p.createWorkContentCopy() p.createWorkContentCopy()
p.ProcessShortcodes(t) p.ProcessShortcodes()
return HandledResult{err: nil} return HandledResult{err: nil}
} }
@ -83,8 +84,8 @@ type asciidocHandler struct {
} }
func (h asciidocHandler) Extensions() []string { return []string{"asciidoc", "adoc", "ad"} } func (h asciidocHandler) Extensions() []string { return []string{"asciidoc", "adoc", "ad"} }
func (h asciidocHandler) PageConvert(p *Page, t tpl.Template) HandledResult { func (h asciidocHandler) PageConvert(p *Page) HandledResult {
return commonConvert(p, t) return commonConvert(p)
} }
type rstHandler struct { type rstHandler struct {
@ -92,8 +93,8 @@ type rstHandler struct {
} }
func (h rstHandler) Extensions() []string { return []string{"rest", "rst"} } func (h rstHandler) Extensions() []string { return []string{"rest", "rst"} }
func (h rstHandler) PageConvert(p *Page, t tpl.Template) HandledResult { func (h rstHandler) PageConvert(p *Page) HandledResult {
return commonConvert(p, t) return commonConvert(p)
} }
type mmarkHandler struct { type mmarkHandler struct {
@ -101,11 +102,11 @@ type mmarkHandler struct {
} }
func (h mmarkHandler) Extensions() []string { return []string{"mmark"} } func (h mmarkHandler) Extensions() []string { return []string{"mmark"} }
func (h mmarkHandler) PageConvert(p *Page, t tpl.Template) HandledResult { func (h mmarkHandler) PageConvert(p *Page) HandledResult {
return commonConvert(p, t) return commonConvert(p)
} }
func commonConvert(p *Page, t tpl.Template) HandledResult { func commonConvert(p *Page) HandledResult {
if p.rendered { if p.rendered {
panic(fmt.Sprintf("Page %q already rendered, does not need conversion", p.BaseFileName())) panic(fmt.Sprintf("Page %q already rendered, does not need conversion", p.BaseFileName()))
} }
@ -113,7 +114,7 @@ func commonConvert(p *Page, t tpl.Template) HandledResult {
// Work on a copy of the raw content from now on. // Work on a copy of the raw content from now on.
p.createWorkContentCopy() p.createWorkContentCopy()
p.ProcessShortcodes(t) p.ProcessShortcodes()
// TODO(bep) these page handlers need to be re-evaluated, as it is hard to // TODO(bep) these page handlers need to be re-evaluated, as it is hard to
// process a page in isolation. See the new preRender func. // process a page in isolation. See the new preRender func.

View file

@ -34,7 +34,6 @@ import (
type HugoSites struct { type HugoSites struct {
Sites []*Site Sites []*Site
tmpl tpl.Template
runMode runmode runMode runmode
multilingual *Multilingual multilingual *Multilingual
@ -50,7 +49,14 @@ type deps struct {
// The logger to use. // The logger to use.
log *jww.Notepad log *jww.Notepad
// TODO(bep) next in line: Viper, hugofs, template tmpl *tpl.GoHTMLTemplate
// TODO(bep) next in line: Viper, hugofs
}
func (d *deps) refreshTemplates(withTemplate ...func(templ tpl.Template) error) {
d.tmpl = tpl.New(d.log, withTemplate...)
d.tmpl.PrintErrors() // TODO(bep) globals error handling
} }
func newDeps(cfg DepsCfg) *deps { func newDeps(cfg DepsCfg) *deps {
@ -59,11 +65,12 @@ func newDeps(cfg DepsCfg) *deps {
if logger == nil { if logger == nil {
// TODO(bep) globals default log level // TODO(bep) globals default log level
//logger = jww.NewNotepad(jww.LevelError, jww.LevelWarn, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime) //logger = jww.NewNotepad(jww.LevelError, jww.LevelWarn, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
logger = jww.NewNotepad(jww.LevelFatal, jww.LevelFatal, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime) logger = jww.NewNotepad(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
} }
return &deps{ return &deps{
log: logger, log: logger,
tmpl: tpl.New(logger, cfg.WithTemplate...),
} }
} }
@ -76,8 +83,16 @@ func newHugoSites(cfg DepsCfg, sites ...*Site) (*HugoSites, error) {
return nil, err return nil, err
} }
var d *deps
if sites[0].deps != nil {
d = sites[0].deps
} else {
d = newDeps(cfg)
}
h := &HugoSites{ h := &HugoSites{
deps: newDeps(cfg), deps: d,
multilingual: langConfig, multilingual: langConfig,
Sites: sites} Sites: sites}
@ -91,18 +106,24 @@ func newHugoSites(cfg DepsCfg, sites ...*Site) (*HugoSites, error) {
// NewHugoSitesFromConfiguration creates HugoSites from the global Viper config. // NewHugoSitesFromConfiguration creates HugoSites from the global Viper config.
// TODO(bep) globals rename this when all the globals are gone. // TODO(bep) globals rename this when all the globals are gone.
func NewHugoSitesFromConfiguration(cfg DepsCfg) (*HugoSites, error) { func NewHugoSitesFromConfiguration(cfg DepsCfg) (*HugoSites, error) {
sites, err := createSitesFromConfig() sites, err := createSitesFromConfig(cfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return newHugoSites(cfg, sites...) return newHugoSites(cfg, sites...)
} }
func createSitesFromConfig() ([]*Site, error) { func createSitesFromConfig(cfg DepsCfg) ([]*Site, error) {
deps := newDeps(cfg)
return createSitesFromDeps(deps)
}
func createSitesFromDeps(deps *deps) ([]*Site, error) {
var sites []*Site var sites []*Site
multilingual := viper.GetStringMap("languages") multilingual := viper.GetStringMap("languages")
if len(multilingual) == 0 { if len(multilingual) == 0 {
sites = append(sites, newSite(helpers.NewDefaultLanguage())) sites = append(sites, newSite(helpers.NewDefaultLanguage(), deps))
} }
if len(multilingual) > 0 { if len(multilingual) > 0 {
@ -115,7 +136,7 @@ func createSitesFromConfig() ([]*Site, error) {
} }
for _, lang := range languages { for _, lang := range languages {
sites = append(sites, newSite(lang)) sites = append(sites, newSite(lang, deps))
} }
} }
@ -134,7 +155,7 @@ func (h *HugoSites) reset() {
func (h *HugoSites) createSitesFromConfig() error { func (h *HugoSites) createSitesFromConfig() error {
sites, err := createSitesFromConfig() sites, err := createSitesFromDeps(h.deps)
if err != nil { if err != nil {
return err return err
@ -192,6 +213,8 @@ type DepsCfg struct {
// The Logger to use. // The Logger to use.
Logger *jww.Notepad Logger *jww.Notepad
WithTemplate []func(templ tpl.Template) error
} }
func (h *HugoSites) renderCrossSitesArtifacts() error { func (h *HugoSites) renderCrossSitesArtifacts() error {

View file

@ -40,7 +40,6 @@ import (
bp "github.com/spf13/hugo/bufferpool" bp "github.com/spf13/hugo/bufferpool"
"github.com/spf13/hugo/hugofs" "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source" "github.com/spf13/hugo/source"
"github.com/spf13/hugo/tpl"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -1284,7 +1283,7 @@ func (p *Page) Render(layout ...string) template.HTML {
l = p.layouts() l = p.layouts()
} }
return tpl.ExecuteTemplateToHTML(p, l...) return p.s.tmpl.ExecuteTemplateToHTML(p, l...)
} }
func (p *Page) determineMarkupType() string { func (p *Page) determineMarkupType() string {
@ -1399,8 +1398,8 @@ func (p *Page) SaveSource() error {
return p.SaveSourceAs(p.FullFilePath()) return p.SaveSourceAs(p.FullFilePath())
} }
func (p *Page) ProcessShortcodes(t tpl.Template) { func (p *Page) ProcessShortcodes() {
tmpContent, tmpContentShortCodes, _ := extractAndRenderShortcodes(string(p.workContent), p, t) tmpContent, tmpContentShortCodes, _ := extractAndRenderShortcodes(string(p.workContent), p)
p.workContent = []byte(tmpContent) p.workContent = []byte(tmpContent)
p.contentShortCodes = tmpContentShortCodes p.contentShortCodes = tmpContentShortCodes
} }

View file

@ -151,8 +151,8 @@ func (sc shortcode) String() string {
// HandleShortcodes does all in one go: extract, render and replace // HandleShortcodes does all in one go: extract, render and replace
// only used for testing // only used for testing
func HandleShortcodes(stringToParse string, page *Page, t tpl.Template) (string, error) { func HandleShortcodes(stringToParse string, page *Page) (string, error) {
tmpContent, tmpShortcodes, err := extractAndRenderShortcodes(stringToParse, page, t) tmpContent, tmpShortcodes, err := extractAndRenderShortcodes(stringToParse, page)
if err != nil { if err != nil {
return "", err return "", err
@ -210,8 +210,8 @@ const innerNewlineRegexp = "\n"
const innerCleanupRegexp = `\A<p>(.*)</p>\n\z` const innerCleanupRegexp = `\A<p>(.*)</p>\n\z`
const innerCleanupExpand = "$1" const innerCleanupExpand = "$1"
func renderShortcode(sc shortcode, parent *ShortcodeWithPage, p *Page, t tpl.Template) string { func renderShortcode(sc shortcode, parent *ShortcodeWithPage, p *Page) string {
tmpl := getShortcodeTemplate(sc.name, t) tmpl := getShortcodeTemplate(sc.name, p.s.tmpl)
if tmpl == nil { if tmpl == nil {
p.s.log.ERROR.Printf("Unable to locate template for shortcode '%s' in page %s", sc.name, p.BaseFileName()) p.s.log.ERROR.Printf("Unable to locate template for shortcode '%s' in page %s", sc.name, p.BaseFileName())
@ -230,7 +230,7 @@ func renderShortcode(sc shortcode, parent *ShortcodeWithPage, p *Page, t tpl.Tem
case string: case string:
inner += innerData.(string) inner += innerData.(string)
case shortcode: case shortcode:
inner += renderShortcode(innerData.(shortcode), data, p, t) inner += renderShortcode(innerData.(shortcode), data, p)
default: default:
p.s.log.ERROR.Printf("Illegal state on shortcode rendering of '%s' in page %s. Illegal type in inner data: %s ", p.s.log.ERROR.Printf("Illegal state on shortcode rendering of '%s' in page %s. Illegal type in inner data: %s ",
sc.name, p.BaseFileName(), reflect.TypeOf(innerData)) sc.name, p.BaseFileName(), reflect.TypeOf(innerData))
@ -280,9 +280,9 @@ func renderShortcode(sc shortcode, parent *ShortcodeWithPage, p *Page, t tpl.Tem
return renderShortcodeWithPage(tmpl, data) return renderShortcodeWithPage(tmpl, data)
} }
func extractAndRenderShortcodes(stringToParse string, p *Page, t tpl.Template) (string, map[string]func() (string, error), error) { func extractAndRenderShortcodes(stringToParse string, p *Page) (string, map[string]func() (string, error), error) {
content, shortcodes, err := extractShortcodes(stringToParse, p, t) content, shortcodes, err := extractShortcodes(stringToParse, p)
if err != nil { if err != nil {
// try to render what we have whilst logging the error // try to render what we have whilst logging the error
@ -293,7 +293,7 @@ func extractAndRenderShortcodes(stringToParse string, p *Page, t tpl.Template) (
// TODO(bep) refactor this // TODO(bep) refactor this
p.shortcodes = shortcodes p.shortcodes = shortcodes
renderedShortcodes := renderShortcodes(shortcodes, p, t) renderedShortcodes := renderShortcodes(shortcodes, p)
return content, renderedShortcodes, err return content, renderedShortcodes, err
@ -315,7 +315,7 @@ func executeShortcodeFuncMap(funcs map[string]func() (string, error)) (map[strin
return result, nil return result, nil
} }
func renderShortcodes(shortcodes map[string]shortcode, p *Page, t tpl.Template) map[string]func() (string, error) { func renderShortcodes(shortcodes map[string]shortcode, p *Page) map[string]func() (string, error) {
renderedShortcodes := make(map[string]func() (string, error)) renderedShortcodes := make(map[string]func() (string, error))
for key, sc := range shortcodes { for key, sc := range shortcodes {
@ -324,7 +324,7 @@ func renderShortcodes(shortcodes map[string]shortcode, p *Page, t tpl.Template)
renderedShortcodes[key] = emptyShortcodeFn renderedShortcodes[key] = emptyShortcodeFn
} else { } else {
shorctode := sc shorctode := sc
renderedShortcodes[key] = func() (string, error) { return renderShortcode(shorctode, nil, p, t), nil } renderedShortcodes[key] = func() (string, error) { return renderShortcode(shorctode, nil, p), nil }
} }
} }
@ -336,7 +336,7 @@ var errShortCodeIllegalState = errors.New("Illegal shortcode state")
// pageTokens state: // pageTokens state:
// - before: positioned just before the shortcode start // - before: positioned just before the shortcode start
// - after: shortcode(s) consumed (plural when they are nested) // - after: shortcode(s) consumed (plural when they are nested)
func extractShortcode(pt *pageTokens, p *Page, t tpl.Template) (shortcode, error) { func extractShortcode(pt *pageTokens, p *Page) (shortcode, error) {
sc := shortcode{} sc := shortcode{}
var isInner = false var isInner = false
@ -357,7 +357,7 @@ Loop:
if cnt > 0 { if cnt > 0 {
// nested shortcode; append it to inner content // nested shortcode; append it to inner content
pt.backup3(currItem, next) pt.backup3(currItem, next)
nested, err := extractShortcode(pt, p, t) nested, err := extractShortcode(pt, p)
if err == nil { if err == nil {
sc.inner = append(sc.inner, nested) sc.inner = append(sc.inner, nested)
} else { } else {
@ -398,7 +398,7 @@ Loop:
sc.inner = append(sc.inner, currItem.val) sc.inner = append(sc.inner, currItem.val)
case tScName: case tScName:
sc.name = currItem.val sc.name = currItem.val
tmpl := getShortcodeTemplate(sc.name, t) tmpl := getShortcodeTemplate(sc.name, p.s.tmpl)
if tmpl == nil { if tmpl == nil {
return sc, fmt.Errorf("Unable to locate template for shortcode '%s' in page %s", sc.name, p.BaseFileName()) return sc, fmt.Errorf("Unable to locate template for shortcode '%s' in page %s", sc.name, p.BaseFileName())
@ -454,7 +454,7 @@ Loop:
return sc, nil return sc, nil
} }
func extractShortcodes(stringToParse string, p *Page, t tpl.Template) (string, map[string]shortcode, error) { func extractShortcodes(stringToParse string, p *Page) (string, map[string]shortcode, error) {
shortCodes := make(map[string]shortcode) shortCodes := make(map[string]shortcode)
@ -492,7 +492,7 @@ Loop:
case tLeftDelimScWithMarkup, tLeftDelimScNoMarkup: case tLeftDelimScWithMarkup, tLeftDelimScNoMarkup:
// let extractShortcode handle left delim (will do so recursively) // let extractShortcode handle left delim (will do so recursively)
pt.backup() pt.backup()
if currShortcode, err = extractShortcode(pt, p, t); err != nil { if currShortcode, err = extractShortcode(pt, p); err != nil {
return result.String(), shortCodes, err return result.String(), shortCodes, err
} }

View file

@ -32,8 +32,13 @@ import (
) )
// TODO(bep) remove // TODO(bep) remove
func pageFromString(in, filename string) (*Page, error) { func pageFromString(in, filename string, withTemplate ...func(templ tpl.Template) error) (*Page, error) {
return pageTestSite.NewPageFrom(strings.NewReader(in), filename) s := pageTestSite
if len(withTemplate) > 0 {
// Have to create a new site
s = NewSiteDefaultLang(withTemplate...)
}
return s.NewPageFrom(strings.NewReader(in), filename)
} }
func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func(templ tpl.Template) error) { func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func(templ tpl.Template) error) {
@ -83,10 +88,10 @@ title: "Title"
} }
func TestShortcodeGoFuzzReports(t *testing.T) { func TestShortcodeGoFuzzReports(t *testing.T) {
tem := tpl.New(logger)
tem.AddInternalShortcode("sc.html", `foo`) p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
p, _ := pageFromString(simplePage, "simple.md") return templ.AddInternalShortcode("sc.html", `foo`)
})
for i, this := range []struct { for i, this := range []struct {
data string data string
@ -94,7 +99,7 @@ func TestShortcodeGoFuzzReports(t *testing.T) {
}{ }{
{"{{</*/", true}, {"{{</*/", true},
} { } {
output, err := HandleShortcodes(this.data, p, tem) output, err := HandleShortcodes(this.data, p)
if this.expectErr && err == nil { if this.expectErr && err == nil {
t.Errorf("[%d] should have errored", i) t.Errorf("[%d] should have errored", i)
@ -304,15 +309,13 @@ func TestHighlight(t *testing.T) {
viper.Set("pygmentsStyle", "bw") viper.Set("pygmentsStyle", "bw")
viper.Set("pygmentsUseClasses", false) viper.Set("pygmentsUseClasses", false)
templ := tpl.New(logger)
code := ` code := `
{{< highlight java >}} {{< highlight java >}}
void do(); void do();
{{< /highlight >}}` {{< /highlight >}}`
p, _ := pageFromString(simplePage, "simple.md") p, _ := pageFromString(simplePage, "simple.md")
output, err := HandleShortcodes(code, p, templ) output, err := HandleShortcodes(code, p)
if err != nil { if err != nil {
t.Fatal("Handle shortcode error", err) t.Fatal("Handle shortcode error", err)
@ -379,16 +382,17 @@ func TestExtractShortcodes(t *testing.T) {
fmt.Sprintf("Hello %sworld%s. And that's it.", testScPlaceholderRegexp, testScPlaceholderRegexp), ""}, fmt.Sprintf("Hello %sworld%s. And that's it.", testScPlaceholderRegexp, testScPlaceholderRegexp), ""},
} { } {
p, _ := pageFromString(simplePage, "simple.md") p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
tem := tpl.New(logger) templ.AddInternalShortcode("tag.html", `tag`)
tem.AddInternalShortcode("tag.html", `tag`) templ.AddInternalShortcode("sc1.html", `sc1`)
tem.AddInternalShortcode("sc1.html", `sc1`) templ.AddInternalShortcode("sc2.html", `sc2`)
tem.AddInternalShortcode("sc2.html", `sc2`) templ.AddInternalShortcode("inner.html", `{{with .Inner }}{{ . }}{{ end }}`)
tem.AddInternalShortcode("inner.html", `{{with .Inner }}{{ . }}{{ end }}`) templ.AddInternalShortcode("inner2.html", `{{.Inner}}`)
tem.AddInternalShortcode("inner2.html", `{{.Inner}}`) templ.AddInternalShortcode("inner3.html", `{{.Inner}}`)
tem.AddInternalShortcode("inner3.html", `{{.Inner}}`) return nil
})
content, shortCodes, err := extractShortcodes(this.input, p, tem) content, shortCodes, err := extractShortcodes(this.input, p)
if b, ok := this.expect.(bool); ok && !b { if b, ok := this.expect.(bool); ok && !b {
if err == nil { if err == nil {

View file

@ -115,19 +115,23 @@ func (s *Site) reset() *Site {
} }
// newSite creates a new site in the given language. // newSite creates a new site in the given language.
func newSite(lang *helpers.Language) *Site { func newSite(lang *helpers.Language, deps *deps, withTemplate ...func(templ tpl.Template) error) *Site {
c := newPageCollections() c := newPageCollections()
// TODO(bep) globals (also see other Site creation places)
deps := newDeps(DepsCfg{})
// TODO(bep) globals // TODO(bep) globals
viper.Set("currentContentLanguage", lang) viper.Set("currentContentLanguage", lang)
if deps == nil {
depsCfg := DepsCfg{WithTemplate: withTemplate}
deps = newDeps(depsCfg)
}
return &Site{deps: deps, Language: lang, PageCollections: c, Info: newSiteInfo(siteBuilderCfg{pageCollections: c, language: lang})} return &Site{deps: deps, Language: lang, PageCollections: c, Info: newSiteInfo(siteBuilderCfg{pageCollections: c, language: lang})}
} }
// NewSiteDefaultLang creates a new site in the default language. // NewSiteDefaultLang creates a new site in the default language.
func NewSiteDefaultLang() *Site { func NewSiteDefaultLang(withTemplate ...func(templ tpl.Template) error) *Site {
return newSite(helpers.NewDefaultLanguage()) return newSite(helpers.NewDefaultLanguage(), nil, withTemplate...)
} }
// Convenience func used in tests. // Convenience func used in tests.
@ -656,24 +660,23 @@ func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
} }
func (s *Site) loadTemplates() {
s.owner.tmpl = tpl.InitializeT(s.log)
s.owner.tmpl.LoadTemplates(s.absLayoutDir())
if s.hasTheme() {
s.owner.tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
}
}
func (s *Site) prepTemplates(withTemplate func(templ tpl.Template) error) error { func (s *Site) prepTemplates(withTemplate func(templ tpl.Template) error) error {
s.loadTemplates()
if withTemplate != nil { wt := func(tmpl tpl.Template) error {
if err := withTemplate(s.owner.tmpl); err != nil { // TODO(bep) global error handling
return err tmpl.LoadTemplates(s.absLayoutDir())
if s.hasTheme() {
tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
} }
if withTemplate != nil {
if err := withTemplate(tmpl); err != nil {
return err
}
}
return nil
} }
s.owner.tmpl.MarkReady() s.refreshTemplates(wt)
return nil return nil
} }
@ -778,6 +781,7 @@ func (s *Site) process(config BuildCfg) (err error) {
if err = s.initialize(); err != nil { if err = s.initialize(); err != nil {
return return
} }
s.prepTemplates(config.withTemplate) s.prepTemplates(config.withTemplate)
s.owner.tmpl.PrintErrors() s.owner.tmpl.PrintErrors()
s.timerStep("initialize & template prep") s.timerStep("initialize & template prep")

View file

@ -109,8 +109,13 @@ func TestRenderWithInvalidTemplate(t *testing.T) {
t.Fatalf("Got build error: %s", err) t.Fatalf("Got build error: %s", err)
} }
if s.log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError) != 1 { errCount := s.log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError)
t.Fatalf("Expecting the template to log an ERROR")
// TODO(bep) globals clean up the template error handling
// The template errors are stored in a slice etc. so we get 4 log entries
// When we should get only 1
if errCount == 0 {
t.Fatalf("Expecting the template to log 1 ERROR, got %d", errCount)
} }
} }

View file

@ -30,10 +30,8 @@ import (
"github.com/yosssi/ace" "github.com/yosssi/ace"
) )
var localTemplates *template.Template // TODO(bep) globals get rid of the rest of the jww.ERR etc.
//var tmpl *GoHTMLTemplate
// TODO(bep) globals get rid of the reset of the jww.ERR etc.
var tmpl *GoHTMLTemplate
// TODO(bep) an interface with hundreds of methods ... remove it. // TODO(bep) an interface with hundreds of methods ... remove it.
// And unexport most of these methods. // And unexport most of these methods.
@ -45,13 +43,13 @@ type Template interface {
GetClone() *template.Template GetClone() *template.Template
LoadTemplates(absPath string) LoadTemplates(absPath string)
LoadTemplatesWithPrefix(absPath, prefix string) LoadTemplatesWithPrefix(absPath, prefix string)
MarkReady()
AddTemplate(name, tpl string) error AddTemplate(name, tpl string) error
AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error
AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error
AddInternalTemplate(prefix, name, tpl string) error AddInternalTemplate(prefix, name, tpl string) error
AddInternalShortcode(name, tpl string) error AddInternalShortcode(name, tpl string) error
PrintErrors() PrintErrors()
Funcs(funcMap template.FuncMap)
} }
type templateErr struct { type templateErr struct {
@ -60,7 +58,8 @@ type templateErr struct {
} }
type GoHTMLTemplate struct { type GoHTMLTemplate struct {
template.Template *template.Template
clone *template.Template clone *template.Template
// a separate storage for the overlays created from cloned master templates. // a separate storage for the overlays created from cloned master templates.
@ -69,41 +68,54 @@ type GoHTMLTemplate struct {
errors []*templateErr errors []*templateErr
funcster *templateFuncster
// TODO(bep) globals template // TODO(bep) globals template
log *jww.Notepad log *jww.Notepad
} }
// InitializeT resets the internal template state to its initial state
func InitializeT(logger *jww.Notepad) *GoHTMLTemplate {
tmpl = New(logger)
return tmpl
}
// New returns a new Hugo Template System // New returns a new Hugo Template System
// with all the additional features, templates & functions // with all the additional features, templates & functions
func New(logger *jww.Notepad) *GoHTMLTemplate { func New(logger *jww.Notepad, withTemplate ...func(templ Template) error) *GoHTMLTemplate {
var templates = &GoHTMLTemplate{ tmpl := &GoHTMLTemplate{
Template: *template.New(""), Template: template.New(""),
overlays: make(map[string]*template.Template), overlays: make(map[string]*template.Template),
errors: make([]*templateErr, 0), errors: make([]*templateErr, 0),
log: logger, log: logger,
} }
localTemplates = &templates.Template tmpl.funcster = newTemplateFuncster(tmpl)
// The URL funcs in the funcMap is somewhat language dependent, // The URL funcs in the funcMap is somewhat language dependent,
// so we need to wait until the language and site config is loaded. // so we need to wait until the language and site config is loaded.
initFuncMap() // TODO(bep) globals
tmpl.funcster.initFuncMap()
for k, v := range funcMap { // TODO(bep) globals
for k, v := range tmpl.funcster.funcMap {
amber.FuncMap[k] = v amber.FuncMap[k] = v
} }
templates.Funcs(funcMap)
templates.LoadEmbedded() tmpl.LoadEmbedded()
return templates
for _, wt := range withTemplate {
err := wt(tmpl)
if err != nil {
tmpl.errors = append(tmpl.errors, &templateErr{"init", err})
}
}
tmpl.markReady()
return tmpl
} }
func partial(name string, contextList ...interface{}) template.HTML { func (t *GoHTMLTemplate) Funcs(funcMap template.FuncMap) {
t.Template.Funcs(funcMap)
}
func (t *GoHTMLTemplate) partial(name string, contextList ...interface{}) template.HTML {
if strings.HasPrefix("partials/", name) { if strings.HasPrefix("partials/", name) {
name = name[8:] name = name[8:]
} }
@ -114,16 +126,16 @@ func partial(name string, contextList ...interface{}) template.HTML {
} else { } else {
context = contextList[0] context = contextList[0]
} }
return ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name) return t.ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name)
} }
func executeTemplate(context interface{}, w io.Writer, layouts ...string) { func (t *GoHTMLTemplate) executeTemplate(context interface{}, w io.Writer, layouts ...string) {
var worked bool var worked bool
for _, layout := range layouts { for _, layout := range layouts {
templ := Lookup(layout) templ := t.Lookup(layout)
if templ == nil { if templ == nil {
layout += ".html" layout += ".html"
templ = Lookup(layout) templ = t.Lookup(layout)
} }
if templ != nil { if templ != nil {
@ -136,28 +148,20 @@ func executeTemplate(context interface{}, w io.Writer, layouts ...string) {
} }
} }
if !worked { if !worked {
tmpl.log.ERROR.Println("Unable to render", layouts) t.log.ERROR.Println("Unable to render", layouts)
tmpl.log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts) t.log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
} }
} }
func ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML { func (t *GoHTMLTemplate) ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML {
b := bp.GetBuffer() b := bp.GetBuffer()
defer bp.PutBuffer(b) defer bp.PutBuffer(b)
executeTemplate(context, b, layouts...) t.executeTemplate(context, b, layouts...)
return template.HTML(b.String()) return template.HTML(b.String())
} }
func Lookup(name string) *template.Template {
return tmpl.Lookup(name)
}
func (t *GoHTMLTemplate) Lookup(name string) *template.Template { func (t *GoHTMLTemplate) Lookup(name string) *template.Template {
if templ := localTemplates.Lookup(name); templ != nil {
return templ
}
if t.overlays != nil { if t.overlays != nil {
if templ, ok := t.overlays[name]; ok { if templ, ok := t.overlays[name]; ok {
return templ return templ
@ -183,9 +187,9 @@ func (t *GoHTMLTemplate) LoadEmbedded() {
t.EmbedTemplates() t.EmbedTemplates()
} }
// MarkReady marks the template as "ready for execution". No changes allowed // markReady marks the template as "ready for execution". No changes allowed
// after this is set. // after this is set.
func (t *GoHTMLTemplate) MarkReady() { func (t *GoHTMLTemplate) markReady() {
if t.clone == nil { if t.clone == nil {
t.clone = template.Must(t.Template.Clone()) t.clone = template.Must(t.Template.Clone())
} }
@ -522,7 +526,7 @@ func (t *GoHTMLTemplate) LoadTemplates(absPath string) {
} }
func (t *GoHTMLTemplate) PrintErrors() { func (t *GoHTMLTemplate) PrintErrors() {
for _, e := range t.errors { for i, e := range t.errors {
t.log.ERROR.Println(e.err) t.log.ERROR.Println(i, ":", e.err)
} }
} }

View file

@ -18,7 +18,6 @@ import (
"html/template" "html/template"
jww "github.com/spf13/jwalterweatherman"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -265,7 +264,3 @@ P2: {{ .Params.LOWER }}
require.Contains(t, result, "P1: P1L") require.Contains(t, result, "P1: P1L")
require.Contains(t, result, "P2: P1L") require.Contains(t, result, "P2: P1L")
} }
func init() {
jww.SetStdoutThreshold(jww.LevelCritical)
}

View file

@ -54,9 +54,19 @@ import (
_ "image/png" _ "image/png"
) )
var ( // Some of the template funcs are'nt entirely stateless.
funcMap template.FuncMap type templateFuncster struct {
) t *GoHTMLTemplate
funcMap template.FuncMap
cachedPartials partialCache
}
func newTemplateFuncster(t *GoHTMLTemplate) *templateFuncster {
return &templateFuncster{
t: t,
cachedPartials: partialCache{p: make(map[string]template.HTML)},
}
}
// eq returns the boolean truth of arg1 == arg2. // eq returns the boolean truth of arg1 == arg2.
func eq(x, y interface{}) bool { func eq(x, y interface{}) bool {
@ -1003,7 +1013,7 @@ func where(seq, key interface{}, args ...interface{}) (interface{}, error) {
} }
// apply takes a map, array, or slice and returns a new slice with the function fname applied over it. // apply takes a map, array, or slice and returns a new slice with the function fname applied over it.
func apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) { func (tf *templateFuncster) apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) {
if seq == nil { if seq == nil {
return make([]interface{}, 0), nil return make([]interface{}, 0), nil
} }
@ -1018,7 +1028,7 @@ func apply(seq interface{}, fname string, args ...interface{}) (interface{}, err
return nil, errors.New("can't iterate over a nil value") return nil, errors.New("can't iterate over a nil value")
} }
fn, found := funcMap[fname] fn, found := tf.funcMap[fname]
if !found { if !found {
return nil, errors.New("can't find function " + fname) return nil, errors.New("can't find function " + fname)
} }
@ -1518,41 +1528,39 @@ type partialCache struct {
// Get retrieves partial output from the cache based upon the partial name. // Get retrieves partial output from the cache based upon the partial name.
// If the partial is not found in the cache, the partial is rendered and added // If the partial is not found in the cache, the partial is rendered and added
// to the cache. // to the cache.
func (c *partialCache) Get(key, name string, context interface{}) (p template.HTML) { func (tf *templateFuncster) Get(key, name string, context interface{}) (p template.HTML) {
var ok bool var ok bool
c.RLock() tf.cachedPartials.RLock()
p, ok = c.p[key] p, ok = tf.cachedPartials.p[key]
c.RUnlock() tf.cachedPartials.RUnlock()
if ok { if ok {
return p return p
} }
c.Lock() tf.cachedPartials.Lock()
if p, ok = c.p[key]; !ok { if p, ok = tf.cachedPartials.p[key]; !ok {
p = partial(name, context) p = tf.t.partial(name, context)
c.p[key] = p tf.cachedPartials.p[key] = p
} }
c.Unlock() tf.cachedPartials.Unlock()
return p return p
} }
var cachedPartials = partialCache{p: make(map[string]template.HTML)}
// partialCached executes and caches partial templates. An optional variant // partialCached executes and caches partial templates. An optional variant
// string parameter (a string slice actually, but be only use a variadic // string parameter (a string slice actually, but be only use a variadic
// argument to make it optional) can be passed so that a given partial can have // argument to make it optional) can be passed so that a given partial can have
// multiple uses. The cache is created with name+variant as the key. // multiple uses. The cache is created with name+variant as the key.
func partialCached(name string, context interface{}, variant ...string) template.HTML { func (tf *templateFuncster) partialCached(name string, context interface{}, variant ...string) template.HTML {
key := name key := name
if len(variant) > 0 { if len(variant) > 0 {
for i := 0; i < len(variant); i++ { for i := 0; i < len(variant); i++ {
key += variant[i] key += variant[i]
} }
} }
return cachedPartials.Get(key, name, context) return tf.Get(key, name, context)
} }
// regexpCache represents a cache of regexp objects protected by a mutex. // regexpCache represents a cache of regexp objects protected by a mutex.
@ -2090,8 +2098,8 @@ func getenv(key interface{}) (string, error) {
return os.Getenv(skey), nil return os.Getenv(skey), nil
} }
func initFuncMap() { func (tf *templateFuncster) initFuncMap() {
funcMap = template.FuncMap{ funcMap := template.FuncMap{
"absURL": absURL, "absURL": absURL,
"absLangURL": func(i interface{}) (template.HTML, error) { "absLangURL": func(i interface{}) (template.HTML, error) {
s, err := cast.ToStringE(i) s, err := cast.ToStringE(i)
@ -2102,7 +2110,7 @@ func initFuncMap() {
}, },
"add": func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '+') }, "add": func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '+') },
"after": after, "after": after,
"apply": apply, "apply": tf.apply,
"base64Decode": base64Decode, "base64Decode": base64Decode,
"base64Encode": base64Encode, "base64Encode": base64Encode,
"chomp": chomp, "chomp": chomp,
@ -2147,8 +2155,8 @@ func initFuncMap() {
"mul": func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '*') }, "mul": func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '*') },
"ne": ne, "ne": ne,
"now": func() time.Time { return time.Now() }, "now": func() time.Time { return time.Now() },
"partial": partial, "partial": tf.t.partial,
"partialCached": partialCached, "partialCached": tf.partialCached,
"plainify": plainify, "plainify": plainify,
"pluralize": pluralize, "pluralize": pluralize,
"querify": querify, "querify": querify,
@ -2195,4 +2203,7 @@ func initFuncMap() {
"i18n": i18nTranslate, "i18n": i18nTranslate,
"T": i18nTranslate, "T": i18nTranslate,
} }
tf.funcMap = funcMap
tf.t.Funcs(funcMap)
} }

View file

@ -1960,40 +1960,43 @@ func TestMarkdownify(t *testing.T) {
} }
func TestApply(t *testing.T) { func TestApply(t *testing.T) {
f := newTestFuncster()
strings := []interface{}{"a\n", "b\n"} strings := []interface{}{"a\n", "b\n"}
noStringers := []interface{}{tstNoStringer{}, tstNoStringer{}} noStringers := []interface{}{tstNoStringer{}, tstNoStringer{}}
chomped, _ := apply(strings, "chomp", ".") chomped, _ := f.apply(strings, "chomp", ".")
assert.Equal(t, []interface{}{template.HTML("a"), template.HTML("b")}, chomped) assert.Equal(t, []interface{}{template.HTML("a"), template.HTML("b")}, chomped)
chomped, _ = apply(strings, "chomp", "c\n") chomped, _ = f.apply(strings, "chomp", "c\n")
assert.Equal(t, []interface{}{template.HTML("c"), template.HTML("c")}, chomped) assert.Equal(t, []interface{}{template.HTML("c"), template.HTML("c")}, chomped)
chomped, _ = apply(nil, "chomp", ".") chomped, _ = f.apply(nil, "chomp", ".")
assert.Equal(t, []interface{}{}, chomped) assert.Equal(t, []interface{}{}, chomped)
_, err := apply(strings, "apply", ".") _, err := f.apply(strings, "apply", ".")
if err == nil { if err == nil {
t.Errorf("apply with apply should fail") t.Errorf("apply with apply should fail")
} }
var nilErr *error var nilErr *error
_, err = apply(nilErr, "chomp", ".") _, err = f.apply(nilErr, "chomp", ".")
if err == nil { if err == nil {
t.Errorf("apply with nil in seq should fail") t.Errorf("apply with nil in seq should fail")
} }
_, err = apply(strings, "dobedobedo", ".") _, err = f.apply(strings, "dobedobedo", ".")
if err == nil { if err == nil {
t.Errorf("apply with unknown func should fail") t.Errorf("apply with unknown func should fail")
} }
_, err = apply(noStringers, "chomp", ".") _, err = f.apply(noStringers, "chomp", ".")
if err == nil { if err == nil {
t.Errorf("apply when func fails should fail") t.Errorf("apply when func fails should fail")
} }
_, err = apply(tstNoStringer{}, "chomp", ".") _, err = f.apply(tstNoStringer{}, "chomp", ".")
if err == nil { if err == nil {
t.Errorf("apply with non-sequence should fail") t.Errorf("apply with non-sequence should fail")
} }
@ -2780,7 +2783,6 @@ func TestPartialCached(t *testing.T) {
data.Params = map[string]interface{}{"langCode": "en"} data.Params = map[string]interface{}{"langCode": "en"}
tstInitTemplates() tstInitTemplates()
InitializeT(logger)
for i, tc := range testCases { for i, tc := range testCases {
var tmp string var tmp string
if tc.variant != "" { if tc.variant != "" {
@ -2831,7 +2833,6 @@ func TestPartialCached(t *testing.T) {
} }
func BenchmarkPartial(b *testing.B) { func BenchmarkPartial(b *testing.B) {
InitializeT(logger)
tmpl, err := New(logger).New("testroot").Parse(`{{ partial "bench1" . }}`) tmpl, err := New(logger).New("testroot").Parse(`{{ partial "bench1" . }}`)
if err != nil { if err != nil {
b.Fatalf("unable to create new html template: %s", err) b.Fatalf("unable to create new html template: %s", err)
@ -2851,7 +2852,6 @@ func BenchmarkPartial(b *testing.B) {
} }
func BenchmarkPartialCached(b *testing.B) { func BenchmarkPartialCached(b *testing.B) {
InitializeT(logger)
tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . }}`) tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . }}`)
if err != nil { if err != nil {
b.Fatalf("unable to create new html template: %s", err) b.Fatalf("unable to create new html template: %s", err)
@ -2871,7 +2871,6 @@ func BenchmarkPartialCached(b *testing.B) {
} }
func BenchmarkPartialCachedVariants(b *testing.B) { func BenchmarkPartialCachedVariants(b *testing.B) {
InitializeT(logger)
tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . "header" }}`) tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . "header" }}`)
if err != nil { if err != nil {
b.Fatalf("unable to create new html template: %s", err) b.Fatalf("unable to create new html template: %s", err)
@ -2889,3 +2888,7 @@ func BenchmarkPartialCachedVariants(b *testing.B) {
buf.Reset() buf.Reset()
} }
} }
func newTestFuncster() *templateFuncster {
return New(logger).funcster
}

View file

@ -55,8 +55,6 @@ html lang=en
for _, root := range []string{"", os.TempDir()} { for _, root := range []string{"", os.TempDir()} {
templ := New(logger)
basePath := this.basePath basePath := this.basePath
innerPath := this.innerPath innerPath := this.innerPath
@ -70,17 +68,20 @@ html lang=en
d := "DATA" d := "DATA"
err := templ.AddAceTemplate("mytemplate.ace", basePath, innerPath, templ := New(logger, func(templ Template) error {
[]byte(this.baseContent), []byte(this.innerContent)) return templ.AddAceTemplate("mytemplate.ace", basePath, innerPath,
[]byte(this.baseContent), []byte(this.innerContent))
if err != nil && this.expectErr == 0 { })
t.Errorf("Test %d with root '%s' errored: %s", i, root, err)
} else if err == nil && this.expectErr == 1 { if len(templ.errors) > 0 && this.expectErr == 0 {
t.Errorf("Test %d with root '%s' errored: %v", i, root, templ.errors)
} else if len(templ.errors) == 0 && this.expectErr == 1 {
t.Errorf("#1 Test %d with root '%s' should have errored", i, root) t.Errorf("#1 Test %d with root '%s' should have errored", i, root)
} }
var buff bytes.Buffer var buff bytes.Buffer
err = templ.ExecuteTemplate(&buff, "mytemplate.html", d) err := templ.ExecuteTemplate(&buff, "mytemplate.html", d)
if err != nil && this.expectErr == 0 { if err != nil && this.expectErr == 0 {
t.Errorf("Test %d with root '%s' errored: %s", i, root, err) t.Errorf("Test %d with root '%s' errored: %s", i, root, err)
@ -245,7 +246,6 @@ func TestTplGoFuzzReports(t *testing.T) {
// Issue #1095 // Issue #1095
{"{{apply .C \"urlize\" " + {"{{apply .C \"urlize\" " +
"\".\"}}", 2}} { "\".\"}}", 2}} {
templ := New(logger)
d := &Data{ d := &Data{
A: 42, A: 42,
@ -258,15 +258,17 @@ func TestTplGoFuzzReports(t *testing.T) {
H: "a,b,c,d,e,f", H: "a,b,c,d,e,f",
} }
err := templ.AddTemplate("fuzz", this.data) templ := New(logger, func(templ Template) error {
return templ.AddTemplate("fuzz", this.data)
if err != nil && this.expectErr == 0 { })
t.Fatalf("Test %d errored: %s", i, err)
} else if err == nil && this.expectErr == 1 { if len(templ.errors) > 0 && this.expectErr == 0 {
t.Fatalf("#1 Test %d should have errored", i) t.Errorf("Test %d errored: %v", i, templ.errors)
} else if len(templ.errors) == 0 && this.expectErr == 1 {
t.Errorf("#1 Test %d should have errored", i)
} }
err := templ.ExecuteTemplate(ioutil.Discard, "fuzz", d)
err = templ.ExecuteTemplate(ioutil.Discard, "fuzz", d)
if err != nil && this.expectErr == 0 { if err != nil && this.expectErr == 0 {
t.Fatalf("Test %d errored: %s", i, err) t.Fatalf("Test %d errored: %s", i, err)

6
vendor/vendor.json vendored
View file

@ -281,10 +281,10 @@
"revisionTime": "2016-11-30T04:45:28Z" "revisionTime": "2016-11-30T04:45:28Z"
}, },
{ {
"checksumSHA1": "HWDERqbEvvfLwzP7Dvh2fvu+sng=", "checksumSHA1": "9pkkhgKp3mwSreiML3plQlQYdLQ=",
"path": "github.com/spf13/jwalterweatherman", "path": "github.com/spf13/jwalterweatherman",
"revision": "bccdd23ae5e51bd2b081814db093646c7af3d34d", "revision": "fa7ca7e836cf3a8bb4ebf799f472c12d7e903d66",
"revisionTime": "2017-01-05T10:55:09Z" "revisionTime": "2017-01-09T13:33:55Z"
}, },
{ {
"checksumSHA1": "zLJY+lsX1e5OO6gRxQd5RfKgdQY=", "checksumSHA1": "zLJY+lsX1e5OO6gRxQd5RfKgdQY=",