diff --git a/collection.go b/collection.go index f6754ff..0fa944b 100644 --- a/collection.go +++ b/collection.go @@ -10,23 +10,23 @@ import ( type Collection struct { Site *Site Name string - Data map[interface{}]interface{} + Data VariableMap Output bool Pages []*Page } -func makeCollection(s *Site, name string, d map[interface{}]interface{}) *Collection { +func makeCollection(s *Site, name string, d VariableMap) *Collection { return &Collection{ Site: s, Name: name, Data: d, - Output: getBool(d, "output", false), + Output: d.Bool("output", false), } } // PageArrayVariableValue returns an array of a page data, for use as the template variable // value of the collection. -func (c *Collection) PageArrayVariableValue() (d []interface{}) { +func (c *Collection) PageArrayVariableValue() (d []VariableMap) { for _, p := range c.Pages { d = append(d, p.PageVariables()) } @@ -34,28 +34,27 @@ func (c *Collection) PageArrayVariableValue() (d []interface{}) { } // Posts returns true if the collection is the special "posts" collection. -func (c *Collection) Posts() bool { +func (c *Collection) IsPosts() bool { return c.Name == "posts" } -// SourceDir returns the source directory for pages in the collection. -func (c *Collection) SourceDir() string { +// Source returns the source directory for pages in the collection. +func (c *Collection) Source() string { return filepath.Join(c.Site.Source, "_"+c.Name) } // ReadPages scans the file system for collection pages, and adds them to c.Pages. func (c *Collection) ReadPages() error { basePath := c.Site.Source - d := map[interface{}]interface{}{ + defaults := mergeVariableMaps(c.Data, VariableMap{ "collection": c.Name, - } - d = mergeMaps(c.Data, d) + }) walkFn := func(path string, info os.FileInfo, err error) error { if err != nil { - // if the issue is simply that the directory doesn't exist, ignore the error + // if the issue is simply that the directory doesn't exist, warn instead of error if os.IsNotExist(err) { - if !c.Posts() { + if !c.IsPosts() { fmt.Println("Missing directory for collection", c.Name) } return nil @@ -69,7 +68,7 @@ func (c *Collection) ReadPages() error { case info.IsDir(): return nil } - p, err := ReadPage(rel, d) + p, err := ReadPage(rel, defaults) switch { case err != nil: return err @@ -81,5 +80,5 @@ func (c *Collection) ReadPages() error { } return nil } - return filepath.Walk(c.SourceDir(), walkFn) + return filepath.Walk(c.Source(), walkFn) } diff --git a/helpers.go b/helpers.go index ddc8e12..1402c40 100644 --- a/helpers.go +++ b/helpers.go @@ -28,7 +28,7 @@ func copyFile(dst, src string, perm os.FileMode) error { return outf.Close() } -func getBool(m map[interface{}]interface{}, k string, defaultValue bool) bool { +func (m VariableMap) Bool(k string, defaultValue bool) bool { if val, found := m[k]; found { if v, ok := val.(bool); ok { return v @@ -37,7 +37,7 @@ func getBool(m map[interface{}]interface{}, k string, defaultValue bool) bool { return defaultValue } -func getString(m map[interface{}]interface{}, k string, defaultValue string) string { +func (m VariableMap) String(k string, defaultValue string) string { if val, found := m[k]; found { if v, ok := val.(string); ok { return v @@ -58,8 +58,8 @@ func LeftPad(s string, n int) string { return string(ws) + s } -func mergeMaps(a map[interface{}]interface{}, b map[interface{}]interface{}) map[interface{}]interface{} { - result := map[interface{}]interface{}{} +func mergeVariableMaps(a VariableMap, b VariableMap) VariableMap { + result := VariableMap{} for k, v := range a { result[k] = v } @@ -69,10 +69,10 @@ func mergeMaps(a map[interface{}]interface{}, b map[interface{}]interface{}) map return result } -// stringMap returns a string-indexed map with the same values as its argument. +// makeVariableMap returns a string-indexed map with the same values as its argument. // Non-strings keys are converted to strings. -func stringMap(m map[interface{}]interface{}) map[string]interface{} { - result := map[string]interface{}{} +func makeVariableMap(m map[interface{}]interface{}) VariableMap { + result := VariableMap{} for k, v := range m { stringer, ok := k.(fmt.Stringer) if ok { diff --git a/helpers_test.go b/helpers_test.go index 6dc1b13..882fd94 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -13,37 +13,37 @@ func TestLeftPad(t *testing.T) { } func TestGetXXX(t *testing.T) { - d := map[interface{}]interface{}{ + d := VariableMap{ "t": true, "f": false, "s": "ss", } - assert.Equal(t, true, getBool(d, "t", true)) - assert.Equal(t, true, getBool(d, "t", false)) - assert.Equal(t, false, getBool(d, "f", true)) - assert.Equal(t, false, getBool(d, "f", true)) - assert.Equal(t, true, getBool(d, "-", true)) - assert.Equal(t, false, getBool(d, "-", false)) - assert.Equal(t, true, getBool(d, "s", true)) - assert.Equal(t, false, getBool(d, "s", false)) + assert.Equal(t, true, d.Bool("t", true)) + assert.Equal(t, true, d.Bool("t", false)) + assert.Equal(t, false, d.Bool("f", true)) + assert.Equal(t, false, d.Bool("f", true)) + assert.Equal(t, true, d.Bool("-", true)) + assert.Equal(t, false, d.Bool("-", false)) + assert.Equal(t, true, d.Bool("s", true)) + assert.Equal(t, false, d.Bool("s", false)) - assert.Equal(t, "ss", getString(d, "s", "-")) - assert.Equal(t, "--", getString(d, "-", "--")) - assert.Equal(t, "--", getString(d, "t", "--")) + assert.Equal(t, "ss", d.String("s", "-")) + assert.Equal(t, "--", d.String("-", "--")) + assert.Equal(t, "--", d.String("t", "--")) } -func TestMergeMaps(t *testing.T) { - m1 := map[interface{}]interface{}{"a": 1, "b": 2} - m2 := map[interface{}]interface{}{"b": 3, "c": 4} - expected := map[interface{}]interface{}{"a": 1, "b": 3, "c": 4} - actual := mergeMaps(m1, m2) +func TestMakeVariableMap(t *testing.T) { + input := map[interface{}]interface{}{"a": 1, 10: 2, false: 3} + expected := VariableMap{"a": 1, "10": 2, "false": 3} + actual := makeVariableMap(input) assert.Equal(t, expected, actual) } -func TestStringMap(t *testing.T) { - input := map[interface{}]interface{}{"a": 1, 10: 2, false: 3} - expected := map[string]interface{}{"a": 1, "10": 2, "false": 3} - actual := stringMap(input) +func TestMergeVariableMaps(t *testing.T) { + m1 := VariableMap{"a": 1, "b": 2} + m2 := VariableMap{"b": 3, "c": 4} + expected := VariableMap{"a": 1, "b": 3, "c": 4} + actual := mergeVariableMaps(m1, m2) assert.Equal(t, expected, actual) } diff --git a/main.go b/main.go index 5d7cbf4..1bbe2d2 100644 --- a/main.go +++ b/main.go @@ -160,9 +160,9 @@ func dataCommand(c *cli.Context) error { // Neuter it. This destroys it as Liquid data, but that's okay in this context. data := site.Variables for _, c := range site.Collections { - data[c.Name] = fmt.Sprintf("", len(data[c.Name].([]interface{}))) + data[c.Name] = fmt.Sprintf("", len(data[c.Name].([]VariableMap))) } - b, _ := yaml.Marshal(stringMap(page.Data())) + b, _ := yaml.Marshal(page.Data()) fmt.Println(string(b)) return nil } diff --git a/page.go b/page.go index 0e65935..733154d 100644 --- a/page.go +++ b/page.go @@ -22,6 +22,8 @@ var ( nonAlphanumericSequenceMatcher = regexp.MustCompile(`[^[:alnum:]]+`) ) +type VariableMap map[string]interface{} + var permalinkStyles = map[string]string{ "date": "/:categories/:year/:month/:day/:title.html", "pretty": "/:categories/:year/:month/:day/:title/", @@ -35,7 +37,7 @@ type Page struct { Permalink string Static bool Published bool - FrontMatter map[interface{}]interface{} + FrontMatter VariableMap Content []byte } @@ -45,11 +47,11 @@ func (p *Page) String() string { } // PageVariables returns metadata for use in the representation of the page as a collection item -func (p *Page) PageVariables() map[interface{}]interface{} { +func (p *Page) PageVariables() VariableMap { if p.Static { - return mergeMaps(p.FrontMatter, p.staticFileData()) + return mergeVariableMaps(p.FrontMatter, p.staticFileData()) } - data := map[interface{}]interface{}{ + data := VariableMap{ "url": p.Permalink, "path": p.Source(), // TODO content title excerpt date id categories tags next previous @@ -68,14 +70,14 @@ func (p *Page) PageVariables() map[interface{}]interface{} { return data } -func (p *Page) staticFileData() map[interface{}]interface{} { +func (p *Page) staticFileData() VariableMap { var ( path = "/" + p.Path base = filepath.Base(path) ext = filepath.Ext(path) ) - return map[interface{}]interface{}{ + return VariableMap{ "path": path, "modified_time": 0, // TODO "name": base, @@ -85,17 +87,17 @@ func (p *Page) staticFileData() map[interface{}]interface{} { } // Data returns the variable context for Liquid evaluation -func (p *Page) Data() map[interface{}]interface{} { - return map[interface{}]interface{}{ +func (p *Page) Data() VariableMap { + return VariableMap{ "page": p.PageVariables(), "site": site.Variables, } } // ReadPage reads a Page from a file, using defaults as the default front matter. -func ReadPage(path string, defaults map[interface{}]interface{}) (p *Page, err error) { +func ReadPage(path string, defaults VariableMap) (p *Page, err error) { var ( - frontMatter map[interface{}]interface{} + frontMatter VariableMap static = true body []byte ) @@ -112,14 +114,14 @@ func ReadPage(path string, defaults map[interface{}]interface{}) (p *Page, err e body = append( regexp.MustCompile(`[^\n\r]+`).ReplaceAllLiteral(source[:match[1]], []byte{}), source[match[1]:]...) - frontMatter = map[interface{}]interface{}{} + frontMatter = VariableMap{} err = yaml.Unmarshal(source[match[2]:match[3]], &frontMatter) if err != nil { err := &os.PathError{Op: "read frontmatter", Path: path, Err: err} return nil, err } - frontMatter = mergeMaps(defaults, frontMatter) + frontMatter = mergeVariableMaps(defaults, frontMatter) } else { frontMatter = defaults body = []byte{} @@ -145,7 +147,7 @@ func ReadPage(path string, defaults map[interface{}]interface{}) (p *Page, err e Path: path, Permalink: permalink, Static: static, - Published: getBool(data, "published", true), + Published: data.Bool("published", true), FrontMatter: data, Content: body, } @@ -182,7 +184,7 @@ func (p *Page) Render(w io.Writer) error { return err } writer := new(bytes.Buffer) - template.Render(writer, stringMap(p.Data())) + template.Render(writer, p.Data()) body := writer.Bytes() if isMarkdown(p.Path) { @@ -198,7 +200,7 @@ func isMarkdown(path string) bool { return site.MarkdownExtensions()[strings.TrimLeft(ext, ".")] } -func permalinkTemplateVariables(path string, data map[interface{}]interface{}) map[string]string { +func permalinkTemplateVariables(path string, data VariableMap) map[string]string { var ( collectionName string localPath = path @@ -206,7 +208,7 @@ func permalinkTemplateVariables(path string, data map[interface{}]interface{}) m root = path[:len(path)-len(ext)] outputExt = ext name = filepath.Base(root) - title = getString(data, "title", name) + title = data.String("title", name) ) if isMarkdown(path) { @@ -235,7 +237,7 @@ func permalinkTemplateVariables(path string, data map[interface{}]interface{}) m } } -func expandPermalinkPattern(pattern string, path string, data map[interface{}]interface{}) (s string, err error) { +func expandPermalinkPattern(pattern string, path string, data VariableMap) (s string, err error) { if p, found := permalinkStyles[pattern]; found { pattern = p } diff --git a/permalinks_test.go b/permalinks_test.go index d182eb7..5dae4c4 100644 --- a/permalinks_test.go +++ b/permalinks_test.go @@ -8,7 +8,7 @@ import ( func TestExpandPermalinkPattern(t *testing.T) { var ( - d = map[interface{}]interface{}{} + d = VariableMap{} path = "/a/b/c.d" mdPath = "/a/b/c.md" ) diff --git a/site.go b/site.go index 1aa729e..b7edbbd 100644 --- a/site.go +++ b/site.go @@ -16,7 +16,7 @@ type Site struct { Dest string Collections []*Collection - Variables map[interface{}]interface{} + Variables VariableMap Paths map[string]*Page // URL path -> *Page config SiteConfig @@ -31,7 +31,7 @@ type SiteConfig struct { // Where things are: Source string Destination string - Collections map[string]interface{} + Collections map[string]VariableMap // Handling Reading Include []string @@ -102,14 +102,14 @@ func (s *Site) ReadConfiguration(source, dest string) error { } func (s *Site) readConfigBytes(bytes []byte) error { - configVariables := map[interface{}]interface{}{} + configVariables := VariableMap{} if err := yaml.Unmarshal(bytes, &s.config); err != nil { return err } if err := yaml.Unmarshal(bytes, &configVariables); err != nil { return err } - s.Variables = mergeMaps(s.Variables, configVariables) + s.Variables = mergeVariableMaps(s.Variables, configVariables) return nil } @@ -158,7 +158,7 @@ func (s *Site) Exclude(path string) bool { // ReadFiles scans the source directory and creates pages and collections. func (s *Site) ReadFiles() error { s.Paths = make(map[string]*Page) - defaults := map[interface{}]interface{}{} + defaults := VariableMap{} walkFn := func(path string, info os.FileInfo, err error) error { if err != nil { @@ -199,11 +199,7 @@ func (s *Site) ReadFiles() error { // pages to the site map, and creates a template site variable for each collection. func (s *Site) readCollections() error { for name, d := range s.config.Collections { - data, ok := d.(map[interface{}]interface{}) - if !ok { - panic("expected collection value to be a map") - } - c := makeCollection(s, name, data) + c := makeCollection(s, name, d) s.Collections = append(s.Collections, c) if c.Output { // TODO always read the pages; just don't build them / include them in routes if err := c.ReadPages(); err != nil {