1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-11-26 23:24:39 +01:00

Introduce a VariableMap type

This commit is contained in:
Oliver Steele 2017-06-14 17:41:15 -04:00
parent 3e68659b3a
commit 089417f1db
7 changed files with 69 additions and 72 deletions

View File

@ -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)
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -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("<elided page data for %d items>", len(data[c.Name].([]interface{})))
data[c.Name] = fmt.Sprintf("<elided page data for %d items>", len(data[c.Name].([]VariableMap)))
}
b, _ := yaml.Marshal(stringMap(page.Data()))
b, _ := yaml.Marshal(page.Data())
fmt.Println(string(b))
return nil
}

36
page.go
View File

@ -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
}

View File

@ -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"
)

16
site.go
View File

@ -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 {