expose site posts and tags to template rendering

Squashed commit of the following:

commit c45734422ef038438a0b6eb4c3065df9cbebcc5e
Author: facundoolano <facundo.olano@gmail.com>
Date:   Tue Feb 13 21:27:47 2024 -0300

    tags test passing

commit 88294405603b2103f9f8cba3b130ad2d40e58a36
Author: facundoolano <facundo.olano@gmail.com>
Date:   Tue Feb 13 21:12:08 2024 -0300

    ensure posts are sorted by date

commit df7c945fcc1490613b6068da3fc487b9ffa44ba5
Author: facundoolano <facundo.olano@gmail.com>
Date:   Tue Feb 13 20:55:51 2024 -0300

    prepare to test tags

commit 3d7c3f380c3933d6cd9f949d3d2cf9ef144bd6ae
Author: facundoolano <facundo.olano@gmail.com>
Date:   Tue Feb 13 20:51:08 2024 -0300

    preload context in site fields

commit a3afaacc67a9d748cfd42ecbeb7b2d302c3560b2
Author: facundoolano <facundo.olano@gmail.com>
Date:   Tue Feb 13 20:39:42 2024 -0300

    test passing pending refactor

commit 13de5017f654c8d82b1ba7beb01066a9c22f19fc
Author: facundoolano <facundo.olano@gmail.com>
Date:   Tue Feb 13 20:18:07 2024 -0300

    add (failing) archive test
This commit is contained in:
facundoolano 2024-02-13 21:32:04 -03:00
parent f9e6d211b1
commit 88efad43db
4 changed files with 175 additions and 39 deletions

View file

@ -45,7 +45,7 @@ func Build() error {
os.MkdirAll(targetPath, FILE_RW_MODE)
} else {
if templ, ok := site.TemplateIndex[path]; ok {
if templ, ok := site.Templates[path]; ok {
// if a template was found at source, render it
content, err := site.Render(templ)
if err != nil {

View file

@ -2,11 +2,14 @@ package site
import (
"fmt"
"github.com/facundoolano/blorg/templates"
"io/fs"
"os"
"path/filepath"
"slices"
"strings"
"time"
"github.com/facundoolano/blorg/templates"
)
// TODO review build and other commands and think what can be brought over here.
@ -14,19 +17,20 @@ import (
type Site struct {
config map[string]string // may need to make this interface{} if config gets sophisticated
layouts map[string]templates.Template
posts []templates.Template
pages []templates.Template
tags map[string]*templates.Template
posts []map[string]interface{}
pages []map[string]interface{}
tags map[string][]map[string]interface{}
TemplateIndex map[string]*templates.Template
Templates map[string]*templates.Template
}
func Load(srcDir string, layoutsDir string) (*Site, error) {
// TODO load config from config.yml
site := Site{
layouts: make(map[string]templates.Template),
TemplateIndex: make(map[string]*templates.Template),
config: make(map[string]string),
layouts: make(map[string]templates.Template),
Templates: make(map[string]*templates.Template),
config: make(map[string]string),
tags: make(map[string][]map[string]interface{}),
}
if err := site.loadLayouts(layoutsDir); err != nil {
@ -74,7 +78,7 @@ func (site *Site) loadTemplates(srcDir string) error {
return fmt.Errorf("couldn't read %s", srcDir)
}
return filepath.WalkDir(srcDir, func(path string, entry fs.DirEntry, err error) error {
err = filepath.WalkDir(srcDir, func(path string, entry fs.DirEntry, err error) error {
if !entry.IsDir() {
templ, err := templates.Parse(path)
// if sometime fails or this is not a template, skip
@ -82,23 +86,58 @@ func (site *Site) loadTemplates(srcDir string) error {
return err
}
// set site related (?) metadata. Not sure if this should go elsewhere
relPath, _ := filepath.Rel(srcDir, path)
templ.Metadata["path"] = relPath
templ.Metadata["url"] = "/" + strings.TrimSuffix(relPath, ".html")
templ.Metadata["dir"] = "/" + filepath.Base(relPath)
// posts are templates that can be chronologically sorted --that have a date.
// the rest are pages.
if _, ok := templ.Metadata["date"]; ok {
site.posts = append(site.posts, *templ)
} else {
site.pages = append(site.pages, *templ)
}
site.TemplateIndex[path] = templ
site.posts = append(site.posts, templ.Metadata)
// TODO load tags
// also add to tags index
if tags, ok := templ.Metadata["tags"]; ok {
for _, tag := range tags.([]interface{}) {
tag := tag.(string)
site.tags[tag] = append(site.tags[tag], templ.Metadata)
}
}
} else {
site.pages = append(site.pages, templ.Metadata)
}
site.Templates[path] = templ
}
return nil
})
if err != nil {
return err
}
// sort posts by reverse chronological order
Compare := func(a map[string]interface{}, b map[string]interface{}) int {
return b["date"].(time.Time).Compare(a["date"].(time.Time))
}
slices.SortFunc(site.posts, Compare)
for _, posts := range site.tags {
slices.SortFunc(posts, Compare)
}
return nil
}
func (site Site) Render(templ *templates.Template) (string, error) {
ctx := site.baseContext()
ctx := map[string]interface{}{
"site": map[string]interface{}{
"config": site.config,
"posts": site.posts,
"tags": site.tags,
"pages": site.pages,
},
}
ctx["page"] = templ.Metadata
content, err := templ.Render(ctx)
if err != nil {
@ -120,13 +159,3 @@ func (site Site) Render(templ *templates.Template) (string, error) {
return content, err
}
func (site Site) baseContext() map[string]interface{} {
return map[string]interface{}{
"site": map[string]interface{}{
"config": site.config,
"posts": site.posts,
"tags": site.tags,
},
}
}

View file

@ -7,7 +7,7 @@ import (
)
func TestLoadAndRenderTemplates(t *testing.T) {
root, src, layouts := newProject()
root, layouts, src := newProject()
defer os.RemoveAll(root)
// add two layouts
@ -40,7 +40,8 @@ date: 2024-01-01
---
<p>Hello world!</p>`
file = newFile(src, "hello.html", content)
defer os.Remove(file.Name())
helloPath := file.Name()
defer os.Remove(helloPath)
content = `---
layout: post
@ -50,7 +51,8 @@ date: 2024-02-01
---
<p>goodbye world!</p>`
file = newFile(src, "goodbye.html", content)
defer os.Remove(file.Name())
goodbyePath := file.Name()
defer os.Remove(goodbyePath)
// add a page (no date)
content = `---
@ -59,7 +61,8 @@ title: about
---
<p>about this site</p>`
file = newFile(src, "about.html", content)
defer os.Remove(file.Name())
aboutPath := file.Name()
defer os.Remove(aboutPath)
// add a static file (no front matter)
content = `go away!`
@ -78,8 +81,7 @@ title: about
_, ok = site.layouts["post"]
assert(t, ok)
hello := site.posts[1]
content, err = site.Render(&hello)
content, err = site.Render(site.Templates[helloPath])
assertEqual(t, err, nil)
assertEqual(t, content, `<html>
<head><title>hello world!</title></head>
@ -90,8 +92,7 @@ title: about
</body>
</html>`)
goodbye := site.posts[0]
content, err = site.Render(&goodbye)
content, err = site.Render(site.Templates[goodbyePath])
assertEqual(t, err, nil)
assertEqual(t, content, `<html>
<head><title>goodbye!</title></head>
@ -102,8 +103,7 @@ title: about
</body>
</html>`)
about := site.pages[0]
content, err = site.Render(&about)
content, err = site.Render(site.Templates[aboutPath])
assertEqual(t, err, nil)
assertEqual(t, content, `<html>
<head><title>about</title></head>
@ -115,10 +115,117 @@ title: about
}
func TestRenderArchive(t *testing.T) {
// TODO
root, layouts, src := newProject()
defer os.RemoveAll(root)
content := `---
title: hello world!
date: 2024-01-01
---
<p>Hello world!</p>`
file := newFile(src, "hello.html", content)
defer os.Remove(file.Name())
content = `---
title: goodbye!
date: 2024-02-01
---
<p>goodbye world!</p>`
file = newFile(src, "goodbye.html", content)
defer os.Remove(file.Name())
content = `---
title: an oldie!
date: 2023-01-01
---
<p>oldie</p>`
file = newFile(src, "an-oldie.html", content)
defer os.Remove(file.Name())
// add a page (no date)
content = `---
---
<ul>{% for post in site.posts %}
<li>{{ post.date | date: "%Y-%m-%d" }} <a href="{{ post.url }}">{{post.title}}</a></li>{%endfor%}
</ul>`
file = newFile(src, "about.html", content)
defer os.Remove(file.Name())
site, err := Load(src, layouts)
content, err = site.Render(site.Templates[file.Name()])
assertEqual(t, err, nil)
assertEqual(t, content, `<ul>
<li>2024-02-01 <a href="/goodbye">goodbye!</a></li>
<li>2024-01-01 <a href="/hello">hello world!</a></li>
<li>2023-01-01 <a href="/an-oldie">an oldie!</a></li>
</ul>`)
}
func TestRenderTags(t *testing.T) {
root, layouts, src := newProject()
defer os.RemoveAll(root)
content := `---
title: hello world!
date: 2024-01-01
tags: [web, software]
---
<p>Hello world!</p>`
file := newFile(src, "hello.html", content)
defer os.Remove(file.Name())
content = `---
title: goodbye!
date: 2024-02-01
tags: [web]
---
<p>goodbye world!</p>`
file = newFile(src, "goodbye.html", content)
defer os.Remove(file.Name())
content = `---
title: an oldie!
date: 2023-01-01
tags: [software]
---
<p>oldie</p>`
file = newFile(src, "an-oldie.html", content)
defer os.Remove(file.Name())
// add a page (no date)
content = `---
---
{% for tag in site.tags %}<h1>{{tag[0]}}</h1>{% for post in tag[1] %}
{{post.title}}
{% endfor %}
{% endfor %}
`
file = newFile(src, "about.html", content)
defer os.Remove(file.Name())
site, err := Load(src, layouts)
content, err = site.Render(site.Templates[file.Name()])
assertEqual(t, err, nil)
assertEqual(t, content, `<h1>software</h1>
hello world!
an oldie!
<h1>web</h1>
goodbye!
hello world!
`)
}
func TestRenderPagesInDir(t *testing.T) {
// TODO
}
func TestRenderArchiveWithExcerpts(t *testing.T) {
// TODO
}

View file

@ -52,7 +52,7 @@ func Parse(path string) (*Template, error) {
return nil, errors.New("front matter not closed")
}
var metadata map[string]interface{}
metadata := make(map[string]interface{})
if len(yamlContent) != 0 {
err := yaml.Unmarshal([]byte(yamlContent), &metadata)
if err != nil {