From 93ca7c9e958e34469a337e4efcc7c75774ec50fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sun, 5 Feb 2017 10:20:06 +0700 Subject: all: Refactor to nonglobal Viper, i18n etc. This is a final rewrite that removes all the global state in Hugo, which also enables the use if `t.Parallel` in tests. Updates #2701 Fixes #3016 --- helpers/configProvider.go | 63 ---------------------------------- helpers/content.go | 74 +++++++++++++++++++++++----------------- helpers/content_renderer.go | 57 ++++++++++++++++--------------- helpers/content_renderer_test.go | 34 +++++++++--------- helpers/content_test.go | 49 +++++++++++++++----------- helpers/general.go | 5 ++- helpers/language.go | 46 +++++++++++++++++++------ helpers/language_test.go | 7 ++-- helpers/path.go | 49 +++++++++++++------------- helpers/path_test.go | 44 ++++++++++-------------- helpers/pathspec.go | 54 ++++++++++++++++++----------- helpers/pathspec_test.go | 38 ++++++++++++++------- helpers/pygments.go | 26 +++++++------- helpers/pygments_test.go | 19 ++++++----- helpers/testhelpers_test.go | 37 ++++++++++++++++++++ helpers/url.go | 7 ++-- helpers/url_test.go | 42 ++++++++++++----------- 17 files changed, 348 insertions(+), 303 deletions(-) delete mode 100644 helpers/configProvider.go create mode 100644 helpers/testhelpers_test.go (limited to 'helpers') diff --git a/helpers/configProvider.go b/helpers/configProvider.go deleted file mode 100644 index b96018257..000000000 --- a/helpers/configProvider.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2016-present The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package helpers implements general utility functions that work with -// and on content. The helper functions defined here lay down the -// foundation of how Hugo works with files and filepaths, and perform -// string operations on content. -package helpers - -import ( - "github.com/spf13/viper" -) - -// A cached version of the current ConfigProvider (language) and relatives. These globals -// are unfortunate, but we still have some places that needs this that does -// not have access to the site configuration. -// These values will be set on initialization when rendering a new language. -// -// TODO(bep) Get rid of these. -var ( - currentConfigProvider ConfigProvider -) - -// ConfigProvider provides the configuration settings for Hugo. -type ConfigProvider interface { - GetString(key string) string - GetInt(key string) int - GetBool(key string) bool - GetStringMap(key string) map[string]interface{} - GetStringMapString(key string) map[string]string - Get(key string) interface{} -} - -// Config returns the currently active Hugo config. This will be set -// per site (language) rendered. -func Config() ConfigProvider { - if currentConfigProvider != nil { - return currentConfigProvider - } - // Some tests rely on this. We will fix that, eventually. - return viper.Get("currentContentLanguage").(ConfigProvider) -} - -// InitConfigProviderForCurrentContentLanguage does what it says. -func InitConfigProviderForCurrentContentLanguage() { - currentConfigProvider = viper.Get("CurrentContentLanguage").(ConfigProvider) -} - -// ResetConfigProvider is used in tests. -func ResetConfigProvider() { - currentConfigProvider = nil - -} diff --git a/helpers/content.go b/helpers/content.go index d8130e83f..0eb687af6 100644 --- a/helpers/content.go +++ b/helpers/content.go @@ -24,12 +24,13 @@ import ( "unicode" "unicode/utf8" + "github.com/spf13/hugo/config" + "github.com/miekg/mmark" "github.com/mitchellh/mapstructure" "github.com/russross/blackfriday" bp "github.com/spf13/hugo/bufferpool" jww "github.com/spf13/jwalterweatherman" - "github.com/spf13/viper" "strings" "sync" @@ -41,6 +42,14 @@ var SummaryLength = 70 // SummaryDivider denotes where content summarization should end. The default is "". var SummaryDivider = []byte("") +type ContentSpec struct { + cfg config.Provider +} + +func NewContentSpec(cfg config.Provider) *ContentSpec { + return &ContentSpec{cfg} +} + // Blackfriday holds configuration values for Blackfriday rendering. type Blackfriday struct { Smartypants bool @@ -58,7 +67,7 @@ type Blackfriday struct { } // NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults. -func NewBlackfriday(c ConfigProvider) *Blackfriday { +func (c ContentSpec) NewBlackfriday() *Blackfriday { defaultParam := map[string]interface{}{ "smartypants": true, @@ -75,7 +84,7 @@ func NewBlackfriday(c ConfigProvider) *Blackfriday { ToLowerMap(defaultParam) - siteParam := c.GetStringMap("blackfriday") + siteParam := c.cfg.GetStringMap("blackfriday") siteConfig := make(map[string]interface{}) @@ -187,10 +196,10 @@ func BytesToHTML(b []byte) template.HTML { } // getHTMLRenderer creates a new Blackfriday HTML Renderer with the given configuration. -func getHTMLRenderer(defaultFlags int, ctx *RenderingContext) blackfriday.Renderer { +func (c ContentSpec) getHTMLRenderer(defaultFlags int, ctx *RenderingContext) blackfriday.Renderer { renderParameters := blackfriday.HtmlRendererParameters{ - FootnoteAnchorPrefix: viper.GetString("footnoteAnchorPrefix"), - FootnoteReturnLinkContents: viper.GetString("footnoteReturnLinkContents"), + FootnoteAnchorPrefix: c.cfg.GetString("footnoteAnchorPrefix"), + FootnoteReturnLinkContents: c.cfg.GetString("footnoteReturnLinkContents"), } b := len(ctx.DocumentID) != 0 @@ -265,21 +274,21 @@ func getMarkdownExtensions(ctx *RenderingContext) int { return flags } -func markdownRender(ctx *RenderingContext) []byte { +func (c ContentSpec) markdownRender(ctx *RenderingContext) []byte { if ctx.RenderTOC { return blackfriday.Markdown(ctx.Content, - getHTMLRenderer(blackfriday.HTML_TOC, ctx), + c.getHTMLRenderer(blackfriday.HTML_TOC, ctx), getMarkdownExtensions(ctx)) } - return blackfriday.Markdown(ctx.Content, getHTMLRenderer(0, ctx), + return blackfriday.Markdown(ctx.Content, c.getHTMLRenderer(0, ctx), getMarkdownExtensions(ctx)) } // getMmarkHTMLRenderer creates a new mmark HTML Renderer with the given configuration. -func getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Renderer { +func (c ContentSpec) getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Renderer { renderParameters := mmark.HtmlRendererParameters{ - FootnoteAnchorPrefix: viper.GetString("footnoteAnchorPrefix"), - FootnoteReturnLinkContents: viper.GetString("footnoteReturnLinkContents"), + FootnoteAnchorPrefix: c.cfg.GetString("footnoteAnchorPrefix"), + FootnoteReturnLinkContents: c.cfg.GetString("footnoteReturnLinkContents"), } b := len(ctx.DocumentID) != 0 @@ -294,6 +303,7 @@ func getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Rendere return &HugoMmarkHTMLRenderer{ mmark.HtmlRendererWithParameters(htmlFlags, "", "", renderParameters), + c.cfg, } } @@ -321,8 +331,8 @@ func getMmarkExtensions(ctx *RenderingContext) int { return flags } -func mmarkRender(ctx *RenderingContext) []byte { - return mmark.Parse(ctx.Content, getMmarkHTMLRenderer(0, ctx), +func (c ContentSpec) mmarkRender(ctx *RenderingContext) []byte { + return mmark.Parse(ctx.Content, c.getMmarkHTMLRenderer(0, ctx), getMmarkExtensions(ctx)).Bytes() } @@ -365,42 +375,44 @@ func ExtractTOC(content []byte) (newcontent []byte, toc []byte) { // RenderingContext holds contextual information, like content and configuration, // for a given content rendering. type RenderingContext struct { - Content []byte - PageFmt string - DocumentID string - DocumentName string - Config *Blackfriday - RenderTOC bool - FileResolver FileResolverFunc - LinkResolver LinkResolverFunc - ConfigProvider ConfigProvider - configInit sync.Once + Content []byte + PageFmt string + DocumentID string + DocumentName string + Config *Blackfriday + RenderTOC bool + FileResolver FileResolverFunc + LinkResolver LinkResolverFunc + Cfg config.Provider + configInit sync.Once } -func newViperProvidedRenderingContext() *RenderingContext { - return &RenderingContext{ConfigProvider: viper.GetViper()} +func newRenderingContext(cfg config.Provider) *RenderingContext { + return &RenderingContext{Cfg: cfg} } func (c *RenderingContext) getConfig() *Blackfriday { + // TODO(bep) get rid of this c.configInit.Do(func() { if c.Config == nil { - c.Config = NewBlackfriday(c.ConfigProvider) + cs := NewContentSpec(c.Cfg) + c.Config = cs.NewBlackfriday() } }) return c.Config } // RenderBytes renders a []byte. -func RenderBytes(ctx *RenderingContext) []byte { +func (c ContentSpec) RenderBytes(ctx *RenderingContext) []byte { switch ctx.PageFmt { default: - return markdownRender(ctx) + return c.markdownRender(ctx) case "markdown": - return markdownRender(ctx) + return c.markdownRender(ctx) case "asciidoc": return getAsciidocContent(ctx) case "mmark": - return mmarkRender(ctx) + return c.mmarkRender(ctx) case "rst": return getRstContent(ctx) } diff --git a/helpers/content_renderer.go b/helpers/content_renderer.go index dbc29f431..6082ae945 100644 --- a/helpers/content_renderer.go +++ b/helpers/content_renderer.go @@ -19,8 +19,8 @@ import ( "github.com/miekg/mmark" "github.com/russross/blackfriday" + "github.com/spf13/hugo/config" jww "github.com/spf13/jwalterweatherman" - "github.com/spf13/viper" ) type LinkResolverFunc func(ref string) (string, error) @@ -33,49 +33,49 @@ type HugoHTMLRenderer struct { blackfriday.Renderer } -func (renderer *HugoHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) { - if viper.GetBool("pygmentsCodeFences") && (lang != "" || viper.GetBool("pygmentsCodeFencesGuessSyntax")) { - opts := viper.GetString("pygmentsOptions") +func (r *HugoHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) { + if r.Cfg.GetBool("pygmentsCodeFences") && (lang != "" || r.Cfg.GetBool("pygmentsCodeFencesGuessSyntax")) { + opts := r.Cfg.GetString("pygmentsOptions") str := html.UnescapeString(string(text)) - out.WriteString(Highlight(str, lang, opts)) + out.WriteString(Highlight(r.RenderingContext.Cfg, str, lang, opts)) } else { - renderer.Renderer.BlockCode(out, text, lang) + r.Renderer.BlockCode(out, text, lang) } } -func (renderer *HugoHTMLRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { - if renderer.LinkResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) { +func (r *HugoHTMLRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { + if r.LinkResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) { // Use the blackfriday built in Link handler - renderer.Renderer.Link(out, link, title, content) + r.Renderer.Link(out, link, title, content) } else { // set by SourceRelativeLinksEval - newLink, err := renderer.LinkResolver(string(link)) + newLink, err := r.LinkResolver(string(link)) if err != nil { newLink = string(link) jww.ERROR.Printf("LinkResolver: %s", err) } - renderer.Renderer.Link(out, []byte(newLink), title, content) + r.Renderer.Link(out, []byte(newLink), title, content) } } -func (renderer *HugoHTMLRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { - if renderer.FileResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) { +func (r *HugoHTMLRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { + if r.FileResolver == nil || bytes.HasPrefix(link, []byte("HAHAHUGOSHORTCODE")) { // Use the blackfriday built in Image handler - renderer.Renderer.Image(out, link, title, alt) + r.Renderer.Image(out, link, title, alt) } else { // set by SourceRelativeLinksEval - newLink, err := renderer.FileResolver(string(link)) + newLink, err := r.FileResolver(string(link)) if err != nil { newLink = string(link) jww.ERROR.Printf("FileResolver: %s", err) } - renderer.Renderer.Image(out, []byte(newLink), title, alt) + r.Renderer.Image(out, []byte(newLink), title, alt) } } // ListItem adds task list support to the Blackfriday renderer. -func (renderer *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) { - if !renderer.Config.TaskLists { - renderer.Renderer.ListItem(out, text, flags) +func (r *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) { + if !r.Config.TaskLists { + r.Renderer.ListItem(out, text, flags) return } @@ -87,17 +87,17 @@ func (renderer *HugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags text = append([]byte(``), text[3:]...) } - renderer.Renderer.ListItem(out, text, flags) + r.Renderer.ListItem(out, text, flags) } // List adds task list support to the Blackfriday renderer. -func (renderer *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flags int) { - if !renderer.Config.TaskLists { - renderer.Renderer.List(out, text, flags) +func (r *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flags int) { + if !r.Config.TaskLists { + r.Renderer.List(out, text, flags) return } marker := out.Len() - renderer.Renderer.List(out, text, flags) + r.Renderer.List(out, text, flags) if out.Len() > marker { list := out.Bytes()[marker:] if bytes.Contains(list, []byte("task-list-item")) { @@ -114,13 +114,14 @@ func (renderer *HugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flag // Enabling Hugo to customise the rendering experience type HugoMmarkHTMLRenderer struct { mmark.Renderer + Cfg config.Provider } -func (renderer *HugoMmarkHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string, caption []byte, subfigure bool, callouts bool) { - if viper.GetBool("pygmentsCodeFences") && (lang != "" || viper.GetBool("pygmentsCodeFencesGuessSyntax")) { +func (r *HugoMmarkHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string, caption []byte, subfigure bool, callouts bool) { + if r.Cfg.GetBool("pygmentsCodeFences") && (lang != "" || r.Cfg.GetBool("pygmentsCodeFencesGuessSyntax")) { str := html.UnescapeString(string(text)) - out.WriteString(Highlight(str, lang, "")) + out.WriteString(Highlight(r.Cfg, str, lang, "")) } else { - renderer.Renderer.BlockCode(out, text, lang, caption, subfigure, callouts) + r.Renderer.BlockCode(out, text, lang, caption, subfigure, callouts) } } diff --git a/helpers/content_renderer_test.go b/helpers/content_renderer_test.go index 82168fcfd..ab663966b 100644 --- a/helpers/content_renderer_test.go +++ b/helpers/content_renderer_test.go @@ -22,9 +22,9 @@ import ( ) // Renders a codeblock using Blackfriday -func render(input string) string { - ctx := newViperProvidedRenderingContext() - render := getHTMLRenderer(0, ctx) +func (c ContentSpec) render(input string) string { + ctx := newRenderingContext(c.cfg) + render := c.getHTMLRenderer(0, ctx) buf := &bytes.Buffer{} render.BlockCode(buf, []byte(input), "html") @@ -32,9 +32,9 @@ func render(input string) string { } // Renders a codeblock using Mmark -func renderWithMmark(input string) string { - ctx := newViperProvidedRenderingContext() - render := getMmarkHTMLRenderer(0, ctx) +func (c ContentSpec) renderWithMmark(input string) string { + ctx := newRenderingContext(c.cfg) + render := c.getMmarkHTMLRenderer(0, ctx) buf := &bytes.Buffer{} render.BlockCode(buf, []byte(input), "html", []byte(""), false, false) @@ -59,16 +59,16 @@ func TestCodeFence(t *testing.T) { {false, "", `(?s)^
.*?
\n$`}, } - viper.Reset() - defer viper.Reset() + for i, d := range data { + v := viper.New() - viper.Set("pygmentsStyle", "monokai") - viper.Set("pygmentsUseClasses", true) + v.Set("pygmentsStyle", "monokai") + v.Set("pygmentsUseClasses", true) + v.Set("pygmentsCodeFences", d.enabled) - for i, d := range data { - viper.Set("pygmentsCodeFences", d.enabled) + c := NewContentSpec(v) - result := render(d.input) + result := c.render(d.input) expectedRe, err := regexp.Compile(d.expected) @@ -81,7 +81,7 @@ func TestCodeFence(t *testing.T) { t.Errorf("Test %d failed. BlackFriday enabled:%t, Expected:\n%q got:\n%q", i, d.enabled, d.expected, result) } - result = renderWithMmark(d.input) + result = c.renderWithMmark(d.input) matched = expectedRe.MatchString(result) if !matched { t.Errorf("Test %d failed. Mmark enabled:%t, Expected:\n%q got:\n%q", i, d.enabled, d.expected, result) @@ -90,6 +90,8 @@ func TestCodeFence(t *testing.T) { } func TestBlackfridayTaskList(t *testing.T) { + c := newTestContentSpec() + for i, this := range []struct { markdown string taskListEnabled bool @@ -118,11 +120,11 @@ END `}, } { - blackFridayConfig := NewBlackfriday(viper.GetViper()) + blackFridayConfig := c.NewBlackfriday() blackFridayConfig.TaskLists = this.taskListEnabled ctx := &RenderingContext{Content: []byte(this.markdown), PageFmt: "markdown", Config: blackFridayConfig} - result := string(RenderBytes(ctx)) + result := string(c.RenderBytes(ctx)) if result != this.expect { t.Errorf("[%d] got \n%v but expected \n%v", i, result, this.expect) diff --git a/helpers/content_test.go b/helpers/content_test.go index 67c50f9d6..52bb85097 100644 --- a/helpers/content_test.go +++ b/helpers/content_test.go @@ -21,7 +21,6 @@ import ( "github.com/miekg/mmark" "github.com/russross/blackfriday" - "github.com/spf13/viper" "github.com/stretchr/testify/assert" ) @@ -152,8 +151,9 @@ func TestTruncateWordsByRune(t *testing.T) { } func TestGetHTMLRendererFlags(t *testing.T) { - ctx := newViperProvidedRenderingContext() - renderer := getHTMLRenderer(blackfriday.HTML_USE_XHTML, ctx) + c := newTestContentSpec() + ctx := newRenderingContext(c.cfg) + renderer := c.getHTMLRenderer(blackfriday.HTML_USE_XHTML, ctx) flags := renderer.GetFlags() if flags&blackfriday.HTML_USE_XHTML != blackfriday.HTML_USE_XHTML { t.Errorf("Test flag: %d was not found amongs set flags:%d; Result: %d", blackfriday.HTML_USE_XHTML, flags, flags&blackfriday.HTML_USE_XHTML) @@ -161,6 +161,8 @@ func TestGetHTMLRendererFlags(t *testing.T) { } func TestGetHTMLRendererAllFlags(t *testing.T) { + c := newTestContentSpec() + type data struct { testFlag int } @@ -176,7 +178,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) { {blackfriday.HTML_SMARTYPANTS_LATEX_DASHES}, } defaultFlags := blackfriday.HTML_USE_XHTML - ctx := newViperProvidedRenderingContext() + ctx := newRenderingContext(c.cfg) ctx.Config = ctx.getConfig() ctx.Config.AngledQuotes = true ctx.Config.Fractions = true @@ -186,7 +188,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) { ctx.Config.SmartDashes = true ctx.Config.Smartypants = true ctx.Config.SourceRelativeLinksEval = true - renderer := getHTMLRenderer(defaultFlags, ctx) + renderer := c.getHTMLRenderer(defaultFlags, ctx) actualFlags := renderer.GetFlags() var expectedFlags int //OR-ing flags together... @@ -199,12 +201,13 @@ func TestGetHTMLRendererAllFlags(t *testing.T) { } func TestGetHTMLRendererAnchors(t *testing.T) { - ctx := newViperProvidedRenderingContext() + c := newTestContentSpec() + ctx := newRenderingContext(c.cfg) ctx.DocumentID = "testid" ctx.Config = ctx.getConfig() ctx.Config.PlainIDAnchors = false - actualRenderer := getHTMLRenderer(0, ctx) + actualRenderer := c.getHTMLRenderer(0, ctx) headerBuffer := &bytes.Buffer{} footnoteBuffer := &bytes.Buffer{} expectedFootnoteHref := []byte("href=\"#fn:testid:href\"") @@ -223,11 +226,12 @@ func TestGetHTMLRendererAnchors(t *testing.T) { } func TestGetMmarkHTMLRenderer(t *testing.T) { - ctx := newViperProvidedRenderingContext() + c := newTestContentSpec() + ctx := newRenderingContext(c.cfg) ctx.DocumentID = "testid" ctx.Config = ctx.getConfig() ctx.Config.PlainIDAnchors = false - actualRenderer := getMmarkHTMLRenderer(0, ctx) + actualRenderer := c.getMmarkHTMLRenderer(0, ctx) headerBuffer := &bytes.Buffer{} footnoteBuffer := &bytes.Buffer{} @@ -247,7 +251,8 @@ func TestGetMmarkHTMLRenderer(t *testing.T) { } func TestGetMarkdownExtensionsMasksAreRemovedFromExtensions(t *testing.T) { - ctx := newViperProvidedRenderingContext() + c := newTestContentSpec() + ctx := newRenderingContext(c.cfg) ctx.Config = ctx.getConfig() ctx.Config.Extensions = []string{"headerId"} ctx.Config.ExtensionsMask = []string{"noIntraEmphasis"} @@ -262,7 +267,8 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) { type data struct { testFlag int } - ctx := newViperProvidedRenderingContext() + c := newTestContentSpec() + ctx := newRenderingContext(c.cfg) ctx.Config = ctx.getConfig() ctx.Config.Extensions = []string{""} ctx.Config.ExtensionsMask = []string{""} @@ -294,7 +300,8 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) { } func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) { - ctx := newViperProvidedRenderingContext() + c := newTestContentSpec() + ctx := newRenderingContext(c.cfg) ctx.Config = ctx.getConfig() ctx.Config.Extensions = []string{"definitionLists"} ctx.Config.ExtensionsMask = []string{""} @@ -306,10 +313,11 @@ func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) { } func TestGetMarkdownRenderer(t *testing.T) { - ctx := newViperProvidedRenderingContext() + c := newTestContentSpec() + ctx := newRenderingContext(c.cfg) ctx.Content = []byte("testContent") ctx.Config = ctx.getConfig() - actualRenderedMarkdown := markdownRender(ctx) + actualRenderedMarkdown := c.markdownRender(ctx) expectedRenderedMarkdown := []byte("

testContent

\n") if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) { t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown) @@ -317,10 +325,11 @@ func TestGetMarkdownRenderer(t *testing.T) { } func TestGetMarkdownRendererWithTOC(t *testing.T) { - ctx := &RenderingContext{RenderTOC: true, ConfigProvider: viper.GetViper()} + c := newTestContentSpec() + ctx := &RenderingContext{RenderTOC: true, Cfg: c.cfg} ctx.Content = []byte("testContent") ctx.Config = ctx.getConfig() - actualRenderedMarkdown := markdownRender(ctx) + actualRenderedMarkdown := c.markdownRender(ctx) expectedRenderedMarkdown := []byte("\n\n

testContent

\n") if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) { t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown) @@ -332,7 +341,8 @@ func TestGetMmarkExtensions(t *testing.T) { type data struct { testFlag int } - ctx := newViperProvidedRenderingContext() + c := newTestContentSpec() + ctx := newRenderingContext(c.cfg) ctx.Config = ctx.getConfig() ctx.Config.Extensions = []string{"tables"} ctx.Config.ExtensionsMask = []string{""} @@ -361,10 +371,11 @@ func TestGetMmarkExtensions(t *testing.T) { } func TestMmarkRender(t *testing.T) { - ctx := newViperProvidedRenderingContext() + c := newTestContentSpec() + ctx := newRenderingContext(c.cfg) ctx.Content = []byte("testContent") ctx.Config = ctx.getConfig() - actualRenderedMarkdown := mmarkRender(ctx) + actualRenderedMarkdown := c.mmarkRender(ctx) expectedRenderedMarkdown := []byte("

testContent

\n") if !bytes.Equal(actualRenderedMarkdown, expectedRenderedMarkdown) { t.Errorf("Actual rendered Markdown (%s) did not match expected markdown (%s)", actualRenderedMarkdown, expectedRenderedMarkdown) diff --git a/helpers/general.go b/helpers/general.go index 1088d9dd1..91c25c04c 100644 --- a/helpers/general.go +++ b/helpers/general.go @@ -32,7 +32,6 @@ import ( bp "github.com/spf13/hugo/bufferpool" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/pflag" - "github.com/spf13/viper" ) // FilePathSeparator as defined by os.Separator. @@ -196,8 +195,8 @@ func ReaderContains(r io.Reader, subslice []byte) bool { } // ThemeSet checks whether a theme is in use or not. -func ThemeSet() bool { - return viper.GetString("theme") != "" +func (p *PathSpec) ThemeSet() bool { + return p.theme != "" } type logPrinter interface { diff --git a/helpers/language.go b/helpers/language.go index 000c846b1..9b7136854 100644 --- a/helpers/language.go +++ b/helpers/language.go @@ -19,8 +19,7 @@ import ( "sync" "github.com/spf13/cast" - - "github.com/spf13/viper" + "github.com/spf13/hugo/config" ) // These are the settings that should only be looked up in the global Viper @@ -41,26 +40,28 @@ type Language struct { LanguageName string Title string Weight int - params map[string]interface{} - paramsInit sync.Once + + Cfg config.Provider + params map[string]interface{} + paramsInit sync.Once } func (l *Language) String() string { return l.Lang } -func NewLanguage(lang string) *Language { - return &Language{Lang: lang, params: make(map[string]interface{})} +func NewLanguage(lang string, cfg config.Provider) *Language { + return &Language{Lang: lang, Cfg: cfg, params: make(map[string]interface{})} } -func NewDefaultLanguage() *Language { - defaultLang := viper.GetString("defaultContentLanguage") +func NewDefaultLanguage(cfg config.Provider) *Language { + defaultLang := cfg.GetString("defaultContentLanguage") if defaultLang == "" { defaultLang = "en" } - return NewLanguage(defaultLang) + return NewLanguage(defaultLang, cfg) } type Languages []*Language @@ -83,7 +84,7 @@ func (l *Language) Params() map[string]interface{} { // Merge with global config. // TODO(bep) consider making this part of a constructor func. - globalParams := viper.GetStringMap("params") + globalParams := l.Cfg.GetStringMap("params") for k, v := range globalParams { if _, ok := l.params[k]; !ok { l.params[k] = v @@ -132,5 +133,28 @@ func (l *Language) Get(key string) interface{} { return v } } - return viper.Get(key) + return l.Cfg.Get(key) +} + +// Set sets the value for the key in the language's params. +func (l *Language) Set(key string, value interface{}) { + if l == nil { + panic("language not set") + } + key = strings.ToLower(key) + l.params[key] = value +} + +// IsSet checks whether the key is set in the language or the related config store. +func (l *Language) IsSet(key string) bool { + key = strings.ToLower(key) + + key = strings.ToLower(key) + if !globalOnlySettings[key] { + if _, ok := l.params[key]; ok { + return true + } + } + return l.Cfg.IsSet(key) + } diff --git a/helpers/language_test.go b/helpers/language_test.go index f812d26a2..902177e1a 100644 --- a/helpers/language_test.go +++ b/helpers/language_test.go @@ -21,11 +21,12 @@ import ( ) func TestGetGlobalOnlySetting(t *testing.T) { - lang := NewDefaultLanguage() + v := viper.New() + lang := NewDefaultLanguage(v) lang.SetParam("defaultContentLanguageInSubdir", false) lang.SetParam("paginatePath", "side") - viper.Set("defaultContentLanguageInSubdir", true) - viper.Set("paginatePath", "page") + v.Set("defaultContentLanguageInSubdir", true) + v.Set("paginatePath", "page") require.True(t, lang.GetBool("defaultContentLanguageInSubdir")) require.Equal(t, "side", lang.GetString("paginatePath")) diff --git a/helpers/path.go b/helpers/path.go index 83a91deba..14d98e2c0 100644 --- a/helpers/path.go +++ b/helpers/path.go @@ -24,7 +24,6 @@ import ( "unicode" "github.com/spf13/afero" - "github.com/spf13/viper" "golang.org/x/text/transform" "golang.org/x/text/unicode/norm" ) @@ -153,41 +152,41 @@ func ReplaceExtension(path string, newExt string) string { // AbsPathify creates an absolute path if given a relative path. If already // absolute, the path is just cleaned. -func AbsPathify(inPath string) string { +func (p *PathSpec) AbsPathify(inPath string) string { if filepath.IsAbs(inPath) { return filepath.Clean(inPath) } // TODO(bep): Consider moving workingDir to argument list - return filepath.Clean(filepath.Join(viper.GetString("workingDir"), inPath)) + return filepath.Clean(filepath.Join(p.workingDir, inPath)) } // GetLayoutDirPath returns the absolute path to the layout file dir // for the current Hugo project. -func GetLayoutDirPath() string { - return AbsPathify(viper.GetString("layoutDir")) +func (p *PathSpec) GetLayoutDirPath() string { + return p.AbsPathify(p.layoutDir) } // GetStaticDirPath returns the absolute path to the static file dir // for the current Hugo project. -func GetStaticDirPath() string { - return AbsPathify(viper.GetString("staticDir")) +func (p *PathSpec) GetStaticDirPath() string { + return p.AbsPathify(p.staticDir) } // GetThemeDir gets the root directory of the current theme, if there is one. // If there is no theme, returns the empty string. -func GetThemeDir() string { - if ThemeSet() { - return AbsPathify(filepath.Join(viper.GetString("themesDir"), viper.GetString("theme"))) +func (p *PathSpec) GetThemeDir() string { + if p.ThemeSet() { + return p.AbsPathify(filepath.Join(p.themesDir, p.theme)) } return "" } // GetRelativeThemeDir gets the relative root directory of the current theme, if there is one. // If there is no theme, returns the empty string. -func GetRelativeThemeDir() string { - if ThemeSet() { - return strings.TrimPrefix(filepath.Join(viper.GetString("themesDir"), viper.GetString("theme")), FilePathSeparator) +func (p *PathSpec) GetRelativeThemeDir() string { + if p.ThemeSet() { + return strings.TrimPrefix(filepath.Join(p.themesDir, p.theme), FilePathSeparator) } return "" } @@ -211,13 +210,13 @@ func (p *PathSpec) GetThemeI18nDirPath() (string, error) { } func (p *PathSpec) getThemeDirPath(path string) (string, error) { - if !ThemeSet() { + if !p.ThemeSet() { return "", ErrThemeUndefined } - themeDir := filepath.Join(GetThemeDir(), path) + themeDir := filepath.Join(p.GetThemeDir(), path) if _, err := p.fs.Source.Stat(themeDir); os.IsNotExist(err) { - return "", fmt.Errorf("Unable to find %s directory for theme %s in %s", path, viper.GetString("theme"), themeDir) + return "", fmt.Errorf("Unable to find %s directory for theme %s in %s", path, p.theme, themeDir) } return themeDir, nil @@ -235,7 +234,7 @@ func (p *PathSpec) GetThemesDirPath() string { // It does so by taking either the project's static path or the theme's static // path into consideration. func (p *PathSpec) MakeStaticPathRelative(inPath string) (string, error) { - staticDir := GetStaticDirPath() + staticDir := p.GetStaticDirPath() themeStaticDir := p.GetThemesDirPath() return makePathRelative(inPath, staticDir, themeStaticDir) @@ -360,20 +359,20 @@ func GetRelativePath(path, base string) (final string, err error) { } // PaginateAliasPath creates a path used to access the aliases in the paginator. -func PaginateAliasPath(base string, page int) string { - paginatePath := Config().GetString("paginatePath") - uglify := viper.GetBool("uglyURLs") - var p string +func (p *PathSpec) PaginateAliasPath(base string, page int) string { + paginatePath := p.paginatePath + uglify := p.uglyURLs + var pth string if base != "" { - p = filepath.FromSlash(fmt.Sprintf("/%s/%s/%d", base, paginatePath, page)) + pth = filepath.FromSlash(fmt.Sprintf("/%s/%s/%d", base, paginatePath, page)) } else { - p = filepath.FromSlash(fmt.Sprintf("/%s/%d", paginatePath, page)) + pth = filepath.FromSlash(fmt.Sprintf("/%s/%d", paginatePath, page)) } if uglify { - p += ".html" + pth += ".html" } - return p + return pth } // GuessSection returns the section given a source path. diff --git a/helpers/path_test.go b/helpers/path_test.go index 46141befc..90dd95288 100644 --- a/helpers/path_test.go +++ b/helpers/path_test.go @@ -34,15 +34,7 @@ import ( "github.com/spf13/viper" ) -func initCommonTestConfig() { - viper.Set("currentContentLanguage", NewLanguage("en")) -} - func TestMakePath(t *testing.T) { - viper.Reset() - defer viper.Reset() - initCommonTestConfig() - tests := []struct { input string expected string @@ -64,8 +56,10 @@ func TestMakePath(t *testing.T) { } for _, test := range tests { - viper.Set("removePathAccents", test.removeAccents) - p := NewPathSpec(hugofs.NewMem(), viper.GetViper()) + v := viper.New() + l := NewDefaultLanguage(v) + v.Set("removePathAccents", test.removeAccents) + p := NewPathSpec(hugofs.NewMem(v), l) output := p.MakePath(test.input) if output != test.expected { @@ -75,11 +69,9 @@ func TestMakePath(t *testing.T) { } func TestMakePathSanitized(t *testing.T) { - viper.Reset() - defer viper.Reset() - initCommonTestConfig() - - p := NewPathSpec(hugofs.NewMem(), viper.GetViper()) + v := viper.New() + l := NewDefaultLanguage(v) + p := NewPathSpec(hugofs.NewMem(v), l) tests := []struct { input string @@ -102,12 +94,12 @@ func TestMakePathSanitized(t *testing.T) { } func TestMakePathSanitizedDisablePathToLower(t *testing.T) { - viper.Reset() - defer viper.Reset() + v := viper.New() + + v.Set("disablePathToLower", true) - initCommonTestConfig() - viper.Set("disablePathToLower", true) - p := NewPathSpec(hugofs.NewMem(), viper.GetViper()) + l := NewDefaultLanguage(v) + p := NewPathSpec(hugofs.NewMem(v), l) tests := []struct { input string @@ -553,9 +545,9 @@ func TestAbsPathify(t *testing.T) { for i, d := range data { viper.Reset() // todo see comment in AbsPathify - viper.Set("workingDir", d.workingDir) + ps := newTestDefaultPathSpec("workingDir", d.workingDir) - expected := AbsPathify(d.inPath) + expected := ps.AbsPathify(d.inPath) if d.expected != expected { t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected) } @@ -563,18 +555,18 @@ func TestAbsPathify(t *testing.T) { t.Logf("Running platform specific path tests for %s", runtime.GOOS) if runtime.GOOS == "windows" { for i, d := range windowsData { - viper.Set("workingDir", d.workingDir) + ps := newTestDefaultPathSpec("workingDir", d.workingDir) - expected := AbsPathify(d.inPath) + expected := ps.AbsPathify(d.inPath) if d.expected != expected { t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected) } } } else { for i, d := range unixData { - viper.Set("workingDir", d.workingDir) + ps := newTestDefaultPathSpec("workingDir", d.workingDir) - expected := AbsPathify(d.inPath) + expected := ps.AbsPathify(d.inPath) if d.expected != expected { t.Errorf("Test %d failed. Expected %q but got %q", i, d.expected, expected) } diff --git a/helpers/pathspec.go b/helpers/pathspec.go index 0fc957b3b..ddc183380 100644 --- a/helpers/pathspec.go +++ b/helpers/pathspec.go @@ -16,6 +16,7 @@ package helpers import ( "fmt" + "github.com/spf13/hugo/config" "github.com/spf13/hugo/hugofs" ) @@ -26,11 +27,20 @@ type PathSpec struct { uglyURLs bool canonifyURLs bool - currentContentLanguage *Language + language *Language // pagination path handling paginatePath string + baseURL string + theme string + + // Directories + themesDir string + layoutDir string + workingDir string + staticDir string + // The PathSpec looks up its config settings in both the current language // and then in the global Viper config. // Some settings, the settings listed below, does not make sense to be set @@ -45,31 +55,35 @@ type PathSpec struct { } func (p PathSpec) String() string { - return fmt.Sprintf("PathSpec, language %q, prefix %q, multilingual: %T", p.currentContentLanguage.Lang, p.getLanguagePrefix(), p.multilingual) + return fmt.Sprintf("PathSpec, language %q, prefix %q, multilingual: %T", p.language.Lang, p.getLanguagePrefix(), p.multilingual) } -// NewPathSpec creats a new PathSpec from the given filesystems and ConfigProvider. -func NewPathSpec(fs *hugofs.Fs, config ConfigProvider) *PathSpec { - - currCl, ok := config.Get("currentContentLanguage").(*Language) +// NewPathSpec creats a new PathSpec from the given filesystems and Language. +func NewPathSpec(fs *hugofs.Fs, cfg config.Provider) *PathSpec { - if !ok { - // TODO(bep) globals - currCl = NewLanguage("en") + ps := &PathSpec{ + fs: fs, + disablePathToLower: cfg.GetBool("disablePathToLower"), + removePathAccents: cfg.GetBool("removePathAccents"), + uglyURLs: cfg.GetBool("uglyURLs"), + canonifyURLs: cfg.GetBool("canonifyURLs"), + multilingual: cfg.GetBool("multilingual"), + defaultContentLanguageInSubdir: cfg.GetBool("defaultContentLanguageInSubdir"), + defaultContentLanguage: cfg.GetString("defaultContentLanguage"), + paginatePath: cfg.GetString("paginatePath"), + baseURL: cfg.GetString("baseURL"), + themesDir: cfg.GetString("themesDir"), + layoutDir: cfg.GetString("layoutDir"), + workingDir: cfg.GetString("workingDir"), + staticDir: cfg.GetString("staticDir"), + theme: cfg.GetString("theme"), } - return &PathSpec{ - fs: fs, - disablePathToLower: config.GetBool("disablePathToLower"), - removePathAccents: config.GetBool("removePathAccents"), - uglyURLs: config.GetBool("uglyURLs"), - canonifyURLs: config.GetBool("canonifyURLs"), - multilingual: config.GetBool("multilingual"), - defaultContentLanguageInSubdir: config.GetBool("defaultContentLanguageInSubdir"), - defaultContentLanguage: config.GetString("defaultContentLanguage"), - currentContentLanguage: currCl, - paginatePath: config.GetString("paginatePath"), + if language, ok := cfg.(*Language); ok { + ps.language = language } + + return ps } // PaginatePath returns the configured root path used for paginator pages. diff --git a/helpers/pathspec_test.go b/helpers/pathspec_test.go index 42d828519..07948bb65 100644 --- a/helpers/pathspec_test.go +++ b/helpers/pathspec_test.go @@ -23,17 +23,24 @@ import ( ) func TestNewPathSpecFromConfig(t *testing.T) { - viper.Set("disablePathToLower", true) - viper.Set("removePathAccents", true) - viper.Set("uglyURLs", true) - viper.Set("multilingual", true) - viper.Set("defaultContentLanguageInSubdir", true) - viper.Set("defaultContentLanguage", "no") - viper.Set("currentContentLanguage", NewLanguage("no")) - viper.Set("canonifyURLs", true) - viper.Set("paginatePath", "side") - - p := NewPathSpec(hugofs.NewMem(), viper.GetViper()) + v := viper.New() + l := NewLanguage("no", v) + v.Set("disablePathToLower", true) + v.Set("removePathAccents", true) + v.Set("uglyURLs", true) + v.Set("multilingual", true) + v.Set("defaultContentLanguageInSubdir", true) + v.Set("defaultContentLanguage", "no") + v.Set("canonifyURLs", true) + v.Set("paginatePath", "side") + v.Set("baseURL", "http://base.com") + v.Set("themesDir", "thethemes") + v.Set("layoutDir", "thelayouts") + v.Set("workingDir", "thework") + v.Set("staticDir", "thestatic") + v.Set("theme", "thetheme") + + p := NewPathSpec(hugofs.NewMem(v), l) require.True(t, p.canonifyURLs) require.True(t, p.defaultContentLanguageInSubdir) @@ -42,6 +49,13 @@ func TestNewPathSpecFromConfig(t *testing.T) { require.True(t, p.removePathAccents) require.True(t, p.uglyURLs) require.Equal(t, "no", p.defaultContentLanguage) - require.Equal(t, "no", p.currentContentLanguage.Lang) + require.Equal(t, "no", p.language.Lang) require.Equal(t, "side", p.paginatePath) + + require.Equal(t, "http://base.com", p.baseURL) + require.Equal(t, "thethemes", p.themesDir) + require.Equal(t, "thelayouts", p.layoutDir) + require.Equal(t, "thework", p.workingDir) + require.Equal(t, "thestatic", p.staticDir) + require.Equal(t, "thetheme", p.theme) } diff --git a/helpers/pygments.go b/helpers/pygments.go index 8e6d1a998..051612608 100644 --- a/helpers/pygments.go +++ b/helpers/pygments.go @@ -24,9 +24,9 @@ import ( "sort" "strings" + "github.com/spf13/hugo/config" "github.com/spf13/hugo/hugofs" jww "github.com/spf13/jwalterweatherman" - "github.com/spf13/viper" ) const pygmentsBin = "pygmentize" @@ -41,13 +41,13 @@ func HasPygments() bool { } // Highlight takes some code and returns highlighted code. -func Highlight(code, lang, optsStr string) string { +func Highlight(cfg config.Provider, code, lang, optsStr string) string { if !HasPygments() { jww.WARN.Println("Highlighting requires Pygments to be installed and in the path") return code } - options, err := parsePygmentsOpts(optsStr) + options, err := parsePygmentsOpts(cfg, optsStr) if err != nil { jww.ERROR.Print(err.Error()) @@ -62,8 +62,8 @@ func Highlight(code, lang, optsStr string) string { fs := hugofs.Os - ignoreCache := viper.GetBool("ignoreCache") - cacheDir := viper.GetString("cacheDir") + ignoreCache := cfg.GetBool("ignoreCache") + cacheDir := cfg.GetString("cacheDir") var cachefile string if !ignoreCache && cacheDir != "" { @@ -195,19 +195,19 @@ func createOptionsString(options map[string]string) string { return optionsStr } -func parseDefaultPygmentsOpts() (map[string]string, error) { +func parseDefaultPygmentsOpts(cfg config.Provider) (map[string]string, error) { options := make(map[string]string) - err := parseOptions(options, viper.GetString("pygmentsOptions")) + err := parseOptions(options, cfg.GetString("pygmentsOptions")) if err != nil { return nil, err } - if viper.IsSet("pygmentsStyle") { - options["style"] = viper.GetString("pygmentsStyle") + if cfg.IsSet("pygmentsStyle") { + options["style"] = cfg.GetString("pygmentsStyle") } - if viper.IsSet("pygmentsUseClasses") { - if viper.GetBool("pygmentsUseClasses") { + if cfg.IsSet("pygmentsUseClasses") { + if cfg.GetBool("pygmentsUseClasses") { options["noclasses"] = "false" } else { options["noclasses"] = "true" @@ -222,8 +222,8 @@ func parseDefaultPygmentsOpts() (map[string]string, error) { return options, nil } -func parsePygmentsOpts(in string) (string, error) { - options, err := parseDefaultPygmentsOpts() +func parsePygmentsOpts(cfg config.Provider, in string) (string, error) { + options, err := parseDefaultPygmentsOpts(cfg) if err != nil { return "", err } diff --git a/helpers/pygments_test.go b/helpers/pygments_test.go index 1cca68eac..1fce17859 100644 --- a/helpers/pygments_test.go +++ b/helpers/pygments_test.go @@ -34,11 +34,12 @@ func TestParsePygmentsArgs(t *testing.T) { {"boo=invalid", "foo", false, false}, {"style", "foo", false, false}, } { - viper.Reset() - viper.Set("pygmentsStyle", this.pygmentsStyle) - viper.Set("pygmentsUseClasses", this.pygmentsUseClasses) - result1, err := parsePygmentsOpts(this.in) + v := viper.New() + v.Set("pygmentsStyle", this.pygmentsStyle) + v.Set("pygmentsUseClasses", this.pygmentsUseClasses) + + result1, err := parsePygmentsOpts(v, this.in) if b, ok := this.expect1.(bool); ok && !b { if err == nil { t.Errorf("[%d] parsePygmentArgs didn't return an expected error", i) @@ -70,19 +71,19 @@ func TestParseDefaultPygmentsArgs(t *testing.T) { {"style=foo,noclasses=false", nil, nil, "style=override,noclasses=override"}, {"style=foo,noclasses=false", "override", false, "style=override,noclasses=override"}, } { - viper.Reset() + v := viper.New() - viper.Set("pygmentsOptions", this.pygmentsOptions) + v.Set("pygmentsOptions", this.pygmentsOptions) if s, ok := this.pygmentsStyle.(string); ok { - viper.Set("pygmentsStyle", s) + v.Set("pygmentsStyle", s) } if b, ok := this.pygmentsUseClasses.(bool); ok { - viper.Set("pygmentsUseClasses", b) + v.Set("pygmentsUseClasses", b) } - result, err := parsePygmentsOpts(this.in) + result, err := parsePygmentsOpts(v, this.in) if err != nil { t.Errorf("[%d] parsePygmentArgs failed: %s", i, err) continue diff --git a/helpers/testhelpers_test.go b/helpers/testhelpers_test.go new file mode 100644 index 000000000..303f9feb6 --- /dev/null +++ b/helpers/testhelpers_test.go @@ -0,0 +1,37 @@ +package helpers + +import ( + "github.com/spf13/viper" + + "github.com/spf13/hugo/hugofs" +) + +func newTestPathSpec(fs *hugofs.Fs, v *viper.Viper) *PathSpec { + l := NewDefaultLanguage(v) + return NewPathSpec(fs, l) +} + +func newTestDefaultPathSpec(configKeyValues ...interface{}) *PathSpec { + v := viper.New() + fs := hugofs.NewMem(v) + cfg := newTestCfg(fs) + + for i := 0; i < len(configKeyValues); i += 2 { + cfg.Set(configKeyValues[i].(string), configKeyValues[i+1]) + } + return newTestPathSpec(fs, cfg) +} + +func newTestCfg(fs *hugofs.Fs) *viper.Viper { + v := viper.New() + + v.SetFs(fs.Source) + + return v + +} + +func newTestContentSpec() *ContentSpec { + v := viper.New() + return NewContentSpec(v) +} diff --git a/helpers/url.go b/helpers/url.go index 2f35b059f..3a060eee9 100644 --- a/helpers/url.go +++ b/helpers/url.go @@ -21,7 +21,6 @@ import ( "github.com/PuerkitoBio/purell" jww "github.com/spf13/jwalterweatherman" - "github.com/spf13/viper" ) type pathBridge struct { @@ -158,7 +157,7 @@ func (p *PathSpec) AbsURL(in string, addLanguage bool) string { return in } - baseURL := viper.GetString("baseURL") + baseURL := p.baseURL if strings.HasPrefix(in, "/") { p, err := url.Parse(baseURL) if err != nil { @@ -200,7 +199,7 @@ func (p *PathSpec) getLanguagePrefix() string { defaultLang := p.defaultContentLanguage defaultInSubDir := p.defaultContentLanguageInSubdir - currentLang := p.currentContentLanguage.Lang + currentLang := p.language.Lang if currentLang == "" || (currentLang == defaultLang && !defaultInSubDir) { return "" } @@ -220,7 +219,7 @@ func IsAbsURL(path string) bool { // RelURL creates a URL relative to the BaseURL root. // Note: The result URL will not include the context root if canonifyURLs is enabled. func (p *PathSpec) RelURL(in string, addLanguage bool) string { - baseURL := viper.GetString("baseURL") + baseURL := p.baseURL canonifyURLs := p.canonifyURLs if (!strings.HasPrefix(in, baseURL) && strings.HasPrefix(in, "http")) || strings.HasPrefix(in, "//") { return in diff --git a/helpers/url_test.go b/helpers/url_test.go index b50a9efd8..b53e2e6cc 100644 --- a/helpers/url_test.go +++ b/helpers/url_test.go @@ -25,9 +25,10 @@ import ( ) func TestURLize(t *testing.T) { - initCommonTestConfig() - p := NewPathSpec(hugofs.NewMem(), viper.GetViper()) + v := viper.New() + l := NewDefaultLanguage(v) + p := NewPathSpec(hugofs.NewMem(v), l) tests := []struct { input string @@ -62,11 +63,10 @@ func TestAbsURL(t *testing.T) { } func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) { - viper.Reset() - viper.Set("multilingual", multilingual) - viper.Set("currentContentLanguage", NewLanguage(lang)) - viper.Set("defaultContentLanguage", "en") - viper.Set("defaultContentLanguageInSubdir", defaultInSubDir) + v := viper.New() + v.Set("multilingual", multilingual) + v.Set("defaultContentLanguage", "en") + v.Set("defaultContentLanguageInSubdir", defaultInSubDir) tests := []struct { input string @@ -86,10 +86,10 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, {"http//foo", "http://base/path", "http://base/path/MULTIhttp/foo"}, } - p := NewPathSpec(hugofs.NewMem(), viper.GetViper()) - for _, test := range tests { - viper.Set("baseURL", test.baseURL) + v.Set("baseURL", test.baseURL) + l := NewLanguage(lang, v) + p := NewPathSpec(hugofs.NewMem(v), l) output := p.AbsURL(test.input, addLanguage) expected := test.expected @@ -138,11 +138,10 @@ func TestRelURL(t *testing.T) { } func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) { - viper.Reset() - viper.Set("multilingual", multilingual) - viper.Set("currentContentLanguage", NewLanguage(lang)) - viper.Set("defaultContentLanguage", "en") - viper.Set("defaultContentLanguageInSubdir", defaultInSubDir) + v := viper.New() + v.Set("multilingual", multilingual) + v.Set("defaultContentLanguage", "en") + v.Set("defaultContentLanguageInSubdir", defaultInSubDir) tests := []struct { input string @@ -165,9 +164,10 @@ func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, } for i, test := range tests { - viper.Set("baseURL", test.baseURL) - viper.Set("canonifyURLs", test.canonify) - p := NewPathSpec(hugofs.NewMem(), viper.GetViper()) + v.Set("baseURL", test.baseURL) + v.Set("canonifyURLs", test.canonify) + l := NewLanguage(lang, v) + p := NewPathSpec(hugofs.NewMem(v), l) output := p.RelURL(test.input, addLanguage) @@ -252,8 +252,10 @@ func TestURLPrep(t *testing.T) { } for i, d := range data { - viper.Set("uglyURLs", d.ugly) - p := NewPathSpec(hugofs.NewMem(), viper.GetViper()) + v := viper.New() + v.Set("uglyURLs", d.ugly) + l := NewDefaultLanguage(v) + p := NewPathSpec(hugofs.NewMem(v), l) output := p.URLPrep(d.input) if d.output != output { -- cgit v1.2.3