Account for array type data in data dir merge/override logic
authorVas Sudanagunta <vas@commonkarma.org>
Sun, 11 Feb 2018 23:34:03 +0000 (18:34 -0500)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 12 Feb 2018 16:14:40 +0000 (17:14 +0100)
* Fixes #4366

* Error message to console for unsupported data types

hugolib/datafiles_test.go
hugolib/site.go

index a11929542fa9fce65017d3c61924d2b949daeaf5..de124a77da8603706e427d83f65785d3f5779d59 100644 (file)
@@ -261,8 +261,7 @@ func TestDataDirMultipleSourcesCommingled(t *testing.T) {
        doTestDataDir(t, dd, expected, "theme", "mytheme")
 }
 
-// TODO Issue #4366 unresolved
-func _TestDataDirMultipleSourcesCollidingChildArrays(t *testing.T) {
+func TestDataDirCollidingChildArrays(t *testing.T) {
        t.Parallel()
 
        var dd dataDir
@@ -284,8 +283,7 @@ func _TestDataDirMultipleSourcesCollidingChildArrays(t *testing.T) {
        doTestDataDir(t, dd, expected, "theme", "mytheme")
 }
 
-// TODO Issue #4366 unresolved
-func _TestDataDirMultipleSourcesCollidingTopLevelArrays(t *testing.T) {
+func TestDataDirCollidingTopLevelArrays(t *testing.T) {
        t.Parallel()
 
        var dd dataDir
@@ -302,6 +300,27 @@ func _TestDataDirMultipleSourcesCollidingTopLevelArrays(t *testing.T) {
        doTestDataDir(t, dd, expected, "theme", "mytheme")
 }
 
+func TestDataDirCollidingMapsAndArrays(t *testing.T) {
+       t.Parallel()
+
+       var dd dataDir
+       // on
+       dd.addSource("themes/mytheme/data/a.json", `["1", "2", "3"]`)
+       dd.addSource("themes/mytheme/data/b.json", `{ "film" : "Logan Lucky" }`)
+       dd.addSource("data/a.json", `{ "music" : "Queen's Rebuke" }`)
+       dd.addSource("data/b.json", `["x", "y", "z"]`)
+
+       expected :=
+               map[string]interface{}{
+                       "a": map[string]interface{}{
+                               "music": "Queen's Rebuke",
+                       },
+                       "b": []interface{}{"x", "y", "z"},
+               }
+
+       doTestDataDir(t, dd, expected, "theme", "mytheme")
+}
+
 type dataDir struct {
        sources [][2]string
 }
index 044866cca6648424b8cb5eddd2b512acdf994783..d62662814cdfe032ddfbce839b0d0b9aa90de225 100644 (file)
@@ -812,24 +812,50 @@ func (s *Site) handleDataFile(r source.ReadableFile) error {
                return nil
        }
 
-       // Copy content from current to data when needed
-       if _, ok := current[r.BaseFileName()]; ok {
-               data := data.(map[string]interface{})
-
-               for key, value := range current[r.BaseFileName()].(map[string]interface{}) {
-                       if _, override := data[key]; override {
-                               // filepath.Walk walks the files in lexical order, '/' comes before '.'
-                               // this warning could happen if
-                               // 1. A theme uses the same key; the main data folder wins
-                               // 2. A sub folder uses the same key: the sub folder wins
-                               s.Log.WARN.Printf("Data for key '%s' in path '%s' is overridden in subfolder", key, r.Path())
+       // filepath.Walk walks the files in lexical order, '/' comes before '.'
+       // this warning could happen if
+       // 1. A theme uses the same key; the main data folder wins
+       // 2. A sub folder uses the same key: the sub folder wins
+       higherPrecedentData := current[r.BaseFileName()]
+
+       switch data.(type) {
+       case nil:
+               // hear the crickets?
+
+       case map[string]interface{}:
+
+               switch higherPrecedentData.(type) {
+               case nil:
+                       current[r.BaseFileName()] = data
+               case map[string]interface{}:
+                       // merge maps: insert entries from data for keys that
+                       // don't already exist in higherPrecedentData
+                       higherPrecedentMap := higherPrecedentData.(map[string]interface{})
+                       for key, value := range data.(map[string]interface{}) {
+                               if _, exists := higherPrecedentMap[key]; exists {
+                                       s.Log.WARN.Printf("Data for key '%s' in path '%s' is overridden higher precedence data already in the data tree", key, r.Path())
+                               } else {
+                                       higherPrecedentMap[key] = value
+                               }
                        }
-                       data[key] = value
+               default:
+                       // can't merge: higherPrecedentData is not a map
+                       s.Log.WARN.Printf("The %T data from '%s' overridden by "+
+                               "higher precedence %T data already in the data tree", data, r.Path(), higherPrecedentData)
                }
-       }
 
-       // Insert data
-       current[r.BaseFileName()] = data
+       case []interface{}:
+               if higherPrecedentData == nil {
+                       current[r.BaseFileName()] = data
+               } else {
+                       // we don't merge array data
+                       s.Log.WARN.Printf("The %T data from '%s' overridden by "+
+                               "higher precedence %T data already in the data tree", data, r.Path(), higherPrecedentData)
+               }
+
+       default:
+               s.Log.ERROR.Printf("unexpected data type %T in file %s", data, r.LogicalName())
+       }
 
        return nil
 }