mirror of
https://github.com/facundoolano/jorge.git
synced 2025-01-13 20:03:26 +01:00
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:
parent
f9e6d211b1
commit
88efad43db
4 changed files with 175 additions and 39 deletions
|
@ -45,7 +45,7 @@ func Build() error {
|
||||||
os.MkdirAll(targetPath, FILE_RW_MODE)
|
os.MkdirAll(targetPath, FILE_RW_MODE)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if templ, ok := site.TemplateIndex[path]; ok {
|
if templ, ok := site.Templates[path]; ok {
|
||||||
// if a template was found at source, render it
|
// if a template was found at source, render it
|
||||||
content, err := site.Render(templ)
|
content, err := site.Render(templ)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
81
site/site.go
81
site/site.go
|
@ -2,11 +2,14 @@ package site
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/facundoolano/blorg/templates"
|
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/facundoolano/blorg/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO review build and other commands and think what can be brought over here.
|
// TODO review build and other commands and think what can be brought over here.
|
||||||
|
@ -14,19 +17,20 @@ import (
|
||||||
type Site struct {
|
type Site struct {
|
||||||
config map[string]string // may need to make this interface{} if config gets sophisticated
|
config map[string]string // may need to make this interface{} if config gets sophisticated
|
||||||
layouts map[string]templates.Template
|
layouts map[string]templates.Template
|
||||||
posts []templates.Template
|
posts []map[string]interface{}
|
||||||
pages []templates.Template
|
pages []map[string]interface{}
|
||||||
tags map[string]*templates.Template
|
tags map[string][]map[string]interface{}
|
||||||
|
|
||||||
TemplateIndex map[string]*templates.Template
|
Templates map[string]*templates.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
func Load(srcDir string, layoutsDir string) (*Site, error) {
|
func Load(srcDir string, layoutsDir string) (*Site, error) {
|
||||||
// TODO load config from config.yml
|
// TODO load config from config.yml
|
||||||
site := Site{
|
site := Site{
|
||||||
layouts: make(map[string]templates.Template),
|
layouts: make(map[string]templates.Template),
|
||||||
TemplateIndex: make(map[string]*templates.Template),
|
Templates: make(map[string]*templates.Template),
|
||||||
config: make(map[string]string),
|
config: make(map[string]string),
|
||||||
|
tags: make(map[string][]map[string]interface{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := site.loadLayouts(layoutsDir); err != nil {
|
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 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() {
|
if !entry.IsDir() {
|
||||||
templ, err := templates.Parse(path)
|
templ, err := templates.Parse(path)
|
||||||
// if sometime fails or this is not a template, skip
|
// if sometime fails or this is not a template, skip
|
||||||
|
@ -82,23 +86,58 @@ func (site *Site) loadTemplates(srcDir string) error {
|
||||||
return err
|
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.
|
// posts are templates that can be chronologically sorted --that have a date.
|
||||||
// the rest are pages.
|
// the rest are pages.
|
||||||
if _, ok := templ.Metadata["date"]; ok {
|
if _, ok := templ.Metadata["date"]; ok {
|
||||||
site.posts = append(site.posts, *templ)
|
site.posts = append(site.posts, templ.Metadata)
|
||||||
} else {
|
|
||||||
site.pages = append(site.pages, *templ)
|
|
||||||
}
|
|
||||||
site.TemplateIndex[path] = templ
|
|
||||||
|
|
||||||
// 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
|
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) {
|
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
|
ctx["page"] = templ.Metadata
|
||||||
content, err := templ.Render(ctx)
|
content, err := templ.Render(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -120,13 +159,3 @@ func (site Site) Render(templ *templates.Template) (string, error) {
|
||||||
|
|
||||||
return content, err
|
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,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLoadAndRenderTemplates(t *testing.T) {
|
func TestLoadAndRenderTemplates(t *testing.T) {
|
||||||
root, src, layouts := newProject()
|
root, layouts, src := newProject()
|
||||||
defer os.RemoveAll(root)
|
defer os.RemoveAll(root)
|
||||||
|
|
||||||
// add two layouts
|
// add two layouts
|
||||||
|
@ -40,7 +40,8 @@ date: 2024-01-01
|
||||||
---
|
---
|
||||||
<p>Hello world!</p>`
|
<p>Hello world!</p>`
|
||||||
file = newFile(src, "hello.html", content)
|
file = newFile(src, "hello.html", content)
|
||||||
defer os.Remove(file.Name())
|
helloPath := file.Name()
|
||||||
|
defer os.Remove(helloPath)
|
||||||
|
|
||||||
content = `---
|
content = `---
|
||||||
layout: post
|
layout: post
|
||||||
|
@ -50,7 +51,8 @@ date: 2024-02-01
|
||||||
---
|
---
|
||||||
<p>goodbye world!</p>`
|
<p>goodbye world!</p>`
|
||||||
file = newFile(src, "goodbye.html", content)
|
file = newFile(src, "goodbye.html", content)
|
||||||
defer os.Remove(file.Name())
|
goodbyePath := file.Name()
|
||||||
|
defer os.Remove(goodbyePath)
|
||||||
|
|
||||||
// add a page (no date)
|
// add a page (no date)
|
||||||
content = `---
|
content = `---
|
||||||
|
@ -59,7 +61,8 @@ title: about
|
||||||
---
|
---
|
||||||
<p>about this site</p>`
|
<p>about this site</p>`
|
||||||
file = newFile(src, "about.html", content)
|
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)
|
// add a static file (no front matter)
|
||||||
content = `go away!`
|
content = `go away!`
|
||||||
|
@ -78,8 +81,7 @@ title: about
|
||||||
_, ok = site.layouts["post"]
|
_, ok = site.layouts["post"]
|
||||||
assert(t, ok)
|
assert(t, ok)
|
||||||
|
|
||||||
hello := site.posts[1]
|
content, err = site.Render(site.Templates[helloPath])
|
||||||
content, err = site.Render(&hello)
|
|
||||||
assertEqual(t, err, nil)
|
assertEqual(t, err, nil)
|
||||||
assertEqual(t, content, `<html>
|
assertEqual(t, content, `<html>
|
||||||
<head><title>hello world!</title></head>
|
<head><title>hello world!</title></head>
|
||||||
|
@ -90,8 +92,7 @@ title: about
|
||||||
</body>
|
</body>
|
||||||
</html>`)
|
</html>`)
|
||||||
|
|
||||||
goodbye := site.posts[0]
|
content, err = site.Render(site.Templates[goodbyePath])
|
||||||
content, err = site.Render(&goodbye)
|
|
||||||
assertEqual(t, err, nil)
|
assertEqual(t, err, nil)
|
||||||
assertEqual(t, content, `<html>
|
assertEqual(t, content, `<html>
|
||||||
<head><title>goodbye!</title></head>
|
<head><title>goodbye!</title></head>
|
||||||
|
@ -102,8 +103,7 @@ title: about
|
||||||
</body>
|
</body>
|
||||||
</html>`)
|
</html>`)
|
||||||
|
|
||||||
about := site.pages[0]
|
content, err = site.Render(site.Templates[aboutPath])
|
||||||
content, err = site.Render(&about)
|
|
||||||
assertEqual(t, err, nil)
|
assertEqual(t, err, nil)
|
||||||
assertEqual(t, content, `<html>
|
assertEqual(t, content, `<html>
|
||||||
<head><title>about</title></head>
|
<head><title>about</title></head>
|
||||||
|
@ -115,10 +115,117 @@ title: about
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRenderArchive(t *testing.T) {
|
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) {
|
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
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ func Parse(path string) (*Template, error) {
|
||||||
return nil, errors.New("front matter not closed")
|
return nil, errors.New("front matter not closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
var metadata map[string]interface{}
|
metadata := make(map[string]interface{})
|
||||||
if len(yamlContent) != 0 {
|
if len(yamlContent) != 0 {
|
||||||
err := yaml.Unmarshal([]byte(yamlContent), &metadata)
|
err := yaml.Unmarshal([]byte(yamlContent), &metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue