Fix case issue Viper vs Blackfriday config

There are still work to be done in the case department, but that will have to be another day.

Fixes #2581
See https://github.com/spf13/viper/issues/261
This commit is contained in:
Bjørn Erik Pedersen 2016-10-16 19:28:21 +02:00 committed by GitHub
parent 4d6cd3cb2a
commit 40b1b8f703
7 changed files with 102 additions and 16 deletions

View file

@ -27,7 +27,6 @@ import (
"github.com/miekg/mmark" "github.com/miekg/mmark"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"github.com/russross/blackfriday" "github.com/russross/blackfriday"
"github.com/spf13/cast"
bp "github.com/spf13/hugo/bufferpool" bp "github.com/spf13/hugo/bufferpool"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -60,7 +59,8 @@ type Blackfriday struct {
// NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults. // NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults.
func NewBlackfriday(c ConfigProvider) *Blackfriday { func NewBlackfriday(c ConfigProvider) *Blackfriday {
combinedParam := map[string]interface{}{
defaultParam := map[string]interface{}{
"smartypants": true, "smartypants": true,
"angledQuotes": false, "angledQuotes": false,
"fractions": true, "fractions": true,
@ -73,17 +73,24 @@ func NewBlackfriday(c ConfigProvider) *Blackfriday {
"sourceRelativeLinksProjectFolder": "/docs/content", "sourceRelativeLinksProjectFolder": "/docs/content",
} }
siteParam := c.GetStringMap("blackfriday") ToLowerMap(defaultParam)
if siteParam != nil {
siteConfig := cast.ToStringMap(siteParam)
for key, value := range siteConfig { siteParam := c.GetStringMap("blackfriday")
combinedParam[key] = value
siteConfig := make(map[string]interface{})
for k, v := range defaultParam {
siteConfig[k] = v
}
if siteParam != nil {
for k, v := range siteParam {
siteConfig[k] = v
} }
} }
combinedConfig := &Blackfriday{} combinedConfig := &Blackfriday{}
if err := mapstructure.Decode(combinedParam, combinedConfig); err != nil { if err := mapstructure.Decode(siteConfig, combinedConfig); err != nil {
jww.FATAL.Printf("Failed to get site rendering config\n%s", err.Error()) jww.FATAL.Printf("Failed to get site rendering config\n%s", err.Error())
} }

View file

@ -119,6 +119,29 @@ func ReaderToBytes(lines io.Reader) []byte {
return bc return bc
} }
// ToLowerMap makes all the keys in the given map lower cased and will do so
// recursively.
// Notes:
// * This will modify the map given.
// * Any nested map[interface{}]interface{} will be converted to map[string]interface{}.
func ToLowerMap(m map[string]interface{}) {
for k, v := range m {
switch v.(type) {
case map[interface{}]interface{}:
v = cast.ToStringMap(v)
ToLowerMap(v.(map[string]interface{}))
case map[string]interface{}:
ToLowerMap(v.(map[string]interface{}))
}
lKey := strings.ToLower(k)
if k != lKey {
delete(m, k)
}
m[lKey] = v
}
}
// ReaderToString is the same as ReaderToBytes, but returns a string. // ReaderToString is the same as ReaderToBytes, but returns a string.
func ReaderToString(lines io.Reader) string { func ReaderToString(lines io.Reader) string {
if lines == nil { if lines == nil {

View file

@ -291,3 +291,56 @@ func TestDoArithmetic(t *testing.T) {
} }
} }
} }
func TestToLowerMap(t *testing.T) {
tests := []struct {
input map[string]interface{}
expected map[string]interface{}
}{
{
map[string]interface{}{
"abC": 32,
},
map[string]interface{}{
"abc": 32,
},
},
{
map[string]interface{}{
"abC": 32,
"deF": map[interface{}]interface{}{
23: "A value",
24: map[string]interface{}{
"AbCDe": "A value",
"eFgHi": "Another value",
},
},
"gHi": map[string]interface{}{
"J": 25,
},
},
map[string]interface{}{
"abc": 32,
"def": map[string]interface{}{
"23": "A value",
"24": map[string]interface{}{
"abcde": "A value",
"efghi": "Another value",
},
},
"ghi": map[string]interface{}{
"j": 25,
},
},
},
}
for i, test := range tests {
// ToLowerMap modifies input.
ToLowerMap(test.input)
if !reflect.DeepEqual(test.expected, test.input) {
t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input)
}
}
}

View file

@ -17,7 +17,6 @@ import (
"sync" "sync"
"sort" "sort"
"strings"
"errors" "errors"
"fmt" "fmt"
@ -84,6 +83,7 @@ func toSortedLanguages(l map[string]interface{}) (helpers.Languages, error) {
for lang, langConf := range l { for lang, langConf := range l {
langsMap, err := cast.ToStringMapE(langConf) langsMap, err := cast.ToStringMapE(langConf)
helpers.ToLowerMap(langsMap)
if err != nil { if err != nil {
return nil, fmt.Errorf("Language config is not a map: %T", langConf) return nil, fmt.Errorf("Language config is not a map: %T", langConf)
@ -91,8 +91,7 @@ func toSortedLanguages(l map[string]interface{}) (helpers.Languages, error) {
language := helpers.NewLanguage(lang) language := helpers.NewLanguage(lang)
for k, v := range langsMap { for loki, v := range langsMap {
loki := strings.ToLower(k)
switch loki { switch loki {
case "title": case "title":
language.Title = cast.ToString(v) language.Title = cast.ToString(v)

View file

@ -392,9 +392,11 @@ func (p *Page) getRenderingConfig() *helpers.Blackfriday {
panic(fmt.Sprintf("nil language for %s with source lang %s", p.BaseFileName(), p.lang)) panic(fmt.Sprintf("nil language for %s with source lang %s", p.BaseFileName(), p.lang))
} }
p.renderingConfig = helpers.NewBlackfriday(p.Language()) p.renderingConfig = helpers.NewBlackfriday(p.Language())
if err := mapstructure.Decode(pageParam, p.renderingConfig); err != nil { if err := mapstructure.Decode(pageParam, p.renderingConfig); err != nil {
jww.FATAL.Printf("Failed to get rendering config for %s:\n%s", p.BaseFileName(), err.Error()) jww.FATAL.Printf("Failed to get rendering config for %s:\n%s", p.BaseFileName(), err.Error())
} }
}) })
return p.renderingConfig return p.renderingConfig

View file

@ -337,8 +337,9 @@ func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) {
viper.Set("DisableRSS", false) viper.Set("DisableRSS", false)
viper.Set("RSSUri", "index.xml") viper.Set("RSSUri", "index.xml")
viper.Set("blackfriday", viper.Set("blackfriday",
// TODO(bep) https://github.com/spf13/viper/issues/261
map[string]interface{}{ map[string]interface{}{
"plainIDAnchors": true}) strings.ToLower("plainIDAnchors"): true})
viper.Set("UglyURLs", uglyURLs) viper.Set("UglyURLs", uglyURLs)
@ -964,8 +965,9 @@ func setupLinkingMockSite(t *testing.T) *Site {
viper.Set("PluralizeListTitles", false) viper.Set("PluralizeListTitles", false)
viper.Set("CanonifyURLs", false) viper.Set("CanonifyURLs", false)
viper.Set("blackfriday", viper.Set("blackfriday",
// TODO(bep) see https://github.com/spf13/viper/issues/261
map[string]interface{}{ map[string]interface{}{
"sourceRelativeLinksProjectFolder": "/docs"}) strings.ToLower("sourceRelativeLinksProjectFolder"): "/docs"})
site := &Site{ site := &Site{
Source: &source.InMemorySource{ByteSource: sources}, Source: &source.InMemorySource{ByteSource: sources},

6
vendor/vendor.json vendored
View file

@ -299,10 +299,10 @@
"revisionTime": "2016-10-06T16:53:40Z" "revisionTime": "2016-10-06T16:53:40Z"
}, },
{ {
"checksumSHA1": "+RJudGkFugn3gRJYUIan1Wbugdw=", "checksumSHA1": "2EeKIC5kUssQK8g49DOa78FoMgs=",
"path": "github.com/spf13/viper", "path": "github.com/spf13/viper",
"revision": "51f23d1f1c56a7773ae8f2cfd038f7996ecc9ac2", "revision": "50515b700e02658272117a72bd641b6b7f1222e5",
"revisionTime": "2016-10-10T11:40:38Z" "revisionTime": "2016-10-14T09:24:45Z"
}, },
{ {
"checksumSHA1": "Q2V7Zs3diLmLfmfbiuLpSxETSuY=", "checksumSHA1": "Q2V7Zs3diLmLfmfbiuLpSxETSuY=",