shithub: hugo

Download patch

ref: 773812de6f8870c7ce0355bfafd71d1959a8c199
parent: 4c7e119ca195085c81b5bb3c51dee6409b15ec98
author: Erlend Klakegg Bergheim <[email protected]>
date: Tue Jan 20 18:08:01 EST 2015

Reads data files inside data/ and makes data available in .Site.Data

Fixes #476.

Conflicts:
	hugolib/site.go

--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -119,6 +119,7 @@
 	viper.SetDefault("StaticDir", "static")
 	viper.SetDefault("ArchetypeDir", "archetypes")
 	viper.SetDefault("PublishDir", "public")
+	viper.SetDefault("DataDir", "data")
 	viper.SetDefault("DefaultLayout", "post")
 	viper.SetDefault("BuildDrafts", false)
 	viper.SetDefault("BuildFuture", false)
@@ -287,6 +288,7 @@
 		return nil
 	}
 
+	filepath.Walk(helpers.AbsPathify(viper.GetString("DataDir")), walker)
 	filepath.Walk(helpers.AbsPathify(viper.GetString("ContentDir")), walker)
 	filepath.Walk(helpers.AbsPathify(viper.GetString("LayoutDir")), walker)
 	filepath.Walk(helpers.AbsPathify(viper.GetString("StaticDir")), walker)
--- a/commands/new.go
+++ b/commands/new.go
@@ -130,6 +130,7 @@
 	mkdir(createpath, "content")
 	mkdir(createpath, "archetypes")
 	mkdir(createpath, "static")
+	mkdir(createpath, "data")
 
 	createConfig(createpath, configFormat)
 }
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -34,6 +34,7 @@
 	bp "github.com/spf13/hugo/bufferpool"
 	"github.com/spf13/hugo/helpers"
 	"github.com/spf13/hugo/hugofs"
+	"github.com/spf13/hugo/parser"
 	"github.com/spf13/hugo/source"
 	"github.com/spf13/hugo/target"
 	"github.com/spf13/hugo/tpl"
@@ -81,6 +82,7 @@
 	params      map[string]interface{}
 	draftCount  int
 	futureCount int
+	Data        map[string]interface{}
 }
 
 type targetList struct {
@@ -112,6 +114,7 @@
 	BuildDrafts         bool
 	canonifyUrls        bool
 	paginationPageCount uint64
+	Data            *map[string]interface{}
 }
 
 // SiteSocial is a place to put social details on a site level. These are the
@@ -264,6 +267,61 @@
 	return s.Tmpl.AddTemplate(name, data)
 }
 
+func (s *Site) loadData(fs source.Input) (err error) {
+	s.Data = make(map[string]interface{})
+
+	for _, r := range fs.Files() {
+		// Crawl in data tree to insert data
+		var current map[string]interface{}
+		current = s.Data
+		for _, key := range strings.Split(r.Dir(), string(os.PathSeparator)) {
+			if key != "" {
+				if _, ok := current[key]; !ok {
+					current[key] = make(map[string]interface{})
+				}
+				current = current[key].(map[string]interface{})
+			}
+		}
+
+		// Read data file
+		data, err := readFile(r)
+		if err != nil {
+			return err
+		}
+
+		// 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 {
+					return errors.New("Data in " + r.Path() + " is overrided in subfolder.")
+				} else {
+					data[key] = value
+				}
+			}
+		}
+
+		// Insert data
+		current[r.BaseFileName()] = data
+	}
+
+	return
+}
+
+func readFile(f *source.File) (interface{}, error) {
+	switch f.Extension() {
+	case "yaml", "yml":
+		return parser.HandleYamlMetaData(f.Bytes())
+	case "json":
+		return parser.HandleJsonMetaData(f.Bytes())
+	case "toml":
+		return parser.HandleTomlMetaData(f.Bytes())
+	default:
+		return nil, errors.New("Not supported for data: " + f.Extension())
+	}
+}
+
 func (s *Site) Process() (err error) {
 	if err = s.initialize(); err != nil {
 		return
@@ -270,6 +328,10 @@
 	}
 	s.prepTemplates()
 	s.Tmpl.PrintErrors()
+	if err = s.loadData(&source.Filesystem{Base: s.absDataDir()}); err != nil {
+		return
+	}
+	s.timerStep("load data")
 	s.timerStep("initialize & template prep")
 	if err = s.CreatePages(); err != nil {
 		return
@@ -379,11 +441,16 @@
 		Menus:           &s.Menus,
 		Params:          params,
 		Permalinks:      permalinks,
+		Data:            &s.Data,
 	}
 }
 
 func (s *Site) hasTheme() bool {
 	return viper.GetString("theme") != ""
+}
+
+func (s *Site) absDataDir() string {
+	return helpers.AbsPathify(viper.GetString("DataDir"))
 }
 
 func (s *Site) absThemeDir() string {
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -5,6 +5,7 @@
 	"fmt"
 	"html/template"
 	"io"
+	"os"
 	"path/filepath"
 	"strings"
 	"testing"
@@ -743,5 +744,20 @@
 
 	if s.Taxonomies["categories"]["e"][0].Page.Title != "bza" {
 		t.Errorf("Pages in unexpected order, 'bza' expected first, got '%v'", s.Taxonomies["categories"]["e"][0].Page.Title)
+	}
+}
+
+func TestDataDir(t *testing.T) {
+	sources := []source.ByteSource{
+		{filepath.FromSlash("test" + string(os.PathSeparator) + "foo.yaml"), []byte("bar: foofoo")},
+		{filepath.FromSlash("test.yaml"), []byte("hello:\n- world: foo")},
+	}
+
+	s := &Site{}
+	s.loadData(&source.InMemorySource{ByteSource: sources})
+
+	expected := "map[test:map[hello:[map[world:foo]] foo:map[bar:foofoo]]]"
+	if fmt.Sprint(s.Data) != expected {
+		t.Errorf("Expected structure '%s', got '%s'", expected, s.Data)
 	}
 }