mirror of
https://github.com/danog/gojekyll.git
synced 2024-11-26 23:24:39 +01:00
Simplify Context interface by moving more rendering to its internals
This commit is contained in:
parent
2c8f8e2bde
commit
1317603482
@ -42,12 +42,23 @@ func (c *Collection) Pages() []pages.Page {
|
||||
|
||||
// TemplateVariable returns an array of page objects, for use as the template variable
|
||||
// value of the collection.
|
||||
func (c *Collection) TemplateVariable() []templates.VariableMap {
|
||||
func (c *Collection) TemplateVariable(ctx pages.Context, includeContent bool) ([]templates.VariableMap, error) {
|
||||
d := []templates.VariableMap{}
|
||||
for _, p := range c.Pages() {
|
||||
d = append(d, p.PageVariables())
|
||||
v := p.PageVariables()
|
||||
dp, ok := p.(*pages.DynamicPage)
|
||||
if includeContent && ok {
|
||||
c, err := dp.ComputeContent(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v = templates.MergeVariableMaps(v, templates.VariableMap{
|
||||
"content": string(c),
|
||||
})
|
||||
}
|
||||
d = append(d, v)
|
||||
}
|
||||
return d
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// PermalinkPattern returns the permalink pattern for this collection.
|
||||
|
@ -3,9 +3,9 @@ package collections
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/osteele/gojekyll/liquid"
|
||||
"github.com/osteele/gojekyll/templates"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -18,15 +18,19 @@ var tests = []struct{ in, out string }{
|
||||
|
||||
type MockContext struct{}
|
||||
|
||||
func (c MockContext) FindLayout(_ string, _ *templates.VariableMap) (liquid.Template, error) {
|
||||
func (c MockContext) OutputExt(filename string) string {
|
||||
return path.Ext(filename)
|
||||
}
|
||||
|
||||
func (c MockContext) Render(_ io.Writer, _ []byte, _ string, _ templates.VariableMap) ([]byte, error) {
|
||||
return nil, fmt.Errorf("unimplemented")
|
||||
}
|
||||
func (c MockContext) IsMarkdown(_ string) bool { return true }
|
||||
func (c MockContext) IsSassPath(_ string) bool { return true }
|
||||
func (c MockContext) SassIncludePaths() []string { return []string{} }
|
||||
|
||||
func (c MockContext) ApplyLayout(_ string, _ []byte, _ templates.VariableMap) ([]byte, error) {
|
||||
return nil, fmt.Errorf("unimplemented")
|
||||
}
|
||||
|
||||
func (c MockContext) SiteVariables() templates.VariableMap { return templates.VariableMap{} }
|
||||
func (c MockContext) TemplateEngine() liquid.Engine { return nil }
|
||||
func (c MockContext) WriteSass(io.Writer, []byte) error { return nil }
|
||||
|
||||
func TestNewCollection(t *testing.T) {
|
||||
ctx := MockContext{}
|
||||
|
@ -1,14 +1,13 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/osteele/gojekyll/helpers"
|
||||
"github.com/osteele/gojekyll/liquid"
|
||||
"github.com/osteele/gojekyll/templates"
|
||||
"github.com/russross/blackfriday"
|
||||
)
|
||||
|
||||
// DynamicPage is a static page, that includes frontmatter.
|
||||
@ -45,17 +44,11 @@ func (p *DynamicPage) PageVariables() templates.VariableMap {
|
||||
ext = filepath.Ext(relpath)
|
||||
root = helpers.TrimExt(p.relpath)
|
||||
base = filepath.Base(root)
|
||||
content = p.processed
|
||||
)
|
||||
|
||||
if content == nil {
|
||||
content = &[]byte{}
|
||||
}
|
||||
|
||||
data := templates.VariableMap{
|
||||
"path": relpath,
|
||||
"url": p.Permalink(),
|
||||
"content": content,
|
||||
"path": relpath,
|
||||
"url": p.Permalink(),
|
||||
// TODO output
|
||||
|
||||
// not documented, but present in both collection and non-collection pages
|
||||
@ -105,62 +98,33 @@ func (p *DynamicPage) TemplateContext(ctx Context) templates.VariableMap {
|
||||
|
||||
// Write applies Liquid and Markdown, as appropriate.
|
||||
func (p *DynamicPage) Write(ctx Context, w io.Writer) error {
|
||||
if ctx.IsSassPath(p.relpath) {
|
||||
return ctx.WriteSass(w, p.raw)
|
||||
}
|
||||
if err := p.ComputeContent(ctx); err != nil {
|
||||
if p.processed != nil {
|
||||
_, err := w.Write(*p.processed)
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(*p.processed)
|
||||
b, err := ctx.Render(w, p.raw, p.filename, p.TemplateContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
layout := p.frontMatter.String("layout", "")
|
||||
if layout != "" {
|
||||
b, err = ctx.ApplyLayout(layout, b, p.TemplateContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = w.Write(b)
|
||||
return err
|
||||
}
|
||||
|
||||
// ComputeContent computes the page content.
|
||||
func (p *DynamicPage) ComputeContent(ctx Context) error {
|
||||
if p.processed != nil {
|
||||
return nil
|
||||
}
|
||||
b, err := ctx.TemplateEngine().ParseAndRender(p.raw, p.TemplateContext(ctx))
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *liquid.RenderError:
|
||||
if err.Filename == "" {
|
||||
err.Filename = p.filename
|
||||
}
|
||||
return err
|
||||
default:
|
||||
return helpers.PathError(err, "Liquid Error", p.filename)
|
||||
}
|
||||
}
|
||||
if ctx.IsMarkdown(p.filename) {
|
||||
b = blackfriday.MarkdownCommon(b)
|
||||
}
|
||||
b, err = p.applyLayout(ctx, p.frontMatter, b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.processed = &b
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *DynamicPage) applyLayout(ctx Context, frontMatter templates.VariableMap, body []byte) ([]byte, error) {
|
||||
for {
|
||||
name := frontMatter.String("layout", "")
|
||||
if name == "" {
|
||||
return body, nil
|
||||
}
|
||||
template, err := ctx.FindLayout(name, &frontMatter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vars := templates.MergeVariableMaps(p.TemplateContext(ctx), templates.VariableMap{
|
||||
"content": string(body),
|
||||
"layout": frontMatter,
|
||||
})
|
||||
body, err = template.Render(vars)
|
||||
if err != nil {
|
||||
func (p *DynamicPage) ComputeContent(ctx Context) ([]byte, error) {
|
||||
if p.processed == nil {
|
||||
w := new(bytes.Buffer)
|
||||
if err := p.Write(ctx, w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*p.processed = w.Bytes()
|
||||
}
|
||||
return *p.processed, nil
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/osteele/gojekyll/helpers"
|
||||
"github.com/osteele/gojekyll/liquid"
|
||||
"github.com/osteele/gojekyll/templates"
|
||||
)
|
||||
|
||||
@ -42,13 +41,10 @@ type Page interface {
|
||||
|
||||
// Context provides context information to a Page.
|
||||
type Context interface {
|
||||
FindLayout(relname string, frontMatter *templates.VariableMap) (liquid.Template, error)
|
||||
IsMarkdown(filename string) bool
|
||||
IsSassPath(filename string) bool
|
||||
SassIncludePaths() []string
|
||||
ApplyLayout(string, []byte, templates.VariableMap) ([]byte, error)
|
||||
OutputExt(pathname string) string
|
||||
Render(io.Writer, []byte, string, templates.VariableMap) ([]byte, error)
|
||||
SiteVariables() templates.VariableMap
|
||||
TemplateEngine() liquid.Engine
|
||||
WriteSass(io.Writer, []byte) error
|
||||
}
|
||||
|
||||
// Container is the Page container
|
||||
@ -94,15 +90,7 @@ func NewPageFromFile(ctx Context, c Container, filename string, relpath string,
|
||||
frontMatter: defaults,
|
||||
fileModTime: info.ModTime(),
|
||||
relpath: relpath,
|
||||
}
|
||||
switch {
|
||||
case ctx.IsMarkdown(relpath):
|
||||
// fields.isMarkdown = true
|
||||
fields.outputExt = ".html"
|
||||
case ctx.IsSassPath(relpath):
|
||||
fields.outputExt = ".css"
|
||||
default:
|
||||
fields.outputExt = filepath.Ext(relpath)
|
||||
outputExt: ctx.OutputExt(relpath),
|
||||
}
|
||||
var p Page
|
||||
if string(magic) == "---\n" {
|
||||
|
@ -16,11 +16,9 @@ var tests = []struct{ in, out string }{
|
||||
func TestTagInjector(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
out := new(bytes.Buffer)
|
||||
// bytes.NewBufferString(c.in)
|
||||
w := TagInjector{out, []byte(":insertion:")}
|
||||
_, err := w.Write([]byte(test.in))
|
||||
require.NoError(t, err)
|
||||
// buf.String() // returns a string of what was written to it
|
||||
require.Equal(t, test.out, out.String())
|
||||
}
|
||||
}
|
||||
|
126
sites/render.go
Normal file
126
sites/render.go
Normal file
@ -0,0 +1,126 @@
|
||||
package sites
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/osteele/gojekyll/helpers"
|
||||
"github.com/osteele/gojekyll/liquid"
|
||||
"github.com/osteele/gojekyll/templates"
|
||||
"github.com/russross/blackfriday"
|
||||
)
|
||||
|
||||
// OutputExt returns the output extension.
|
||||
func (s *Site) OutputExt(pathname string) string {
|
||||
switch {
|
||||
case s.IsMarkdown(pathname):
|
||||
return ".html"
|
||||
case s.IsSassPath(pathname):
|
||||
return ".css"
|
||||
default:
|
||||
return filepath.Ext(pathname)
|
||||
}
|
||||
}
|
||||
|
||||
// Render returns nil iff it wrote to the writer
|
||||
func (s *Site) Render(w io.Writer, b []byte, filename string, e templates.VariableMap) ([]byte, error) {
|
||||
if s.IsSassPath(filename) {
|
||||
return nil, s.WriteSass(w, b)
|
||||
}
|
||||
b, err := s.renderTemplate(b, e, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.IsMarkdown(filename) {
|
||||
b = blackfriday.MarkdownCommon(b)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (s *Site) renderTemplate(b []byte, e templates.VariableMap, filename string) ([]byte, error) {
|
||||
b, err := s.liquidEngine.ParseAndRender(b, e)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *liquid.RenderError:
|
||||
if err.Filename == "" {
|
||||
err.Filename = filename
|
||||
}
|
||||
return nil, err
|
||||
default:
|
||||
return nil, helpers.PathError(err, "Liquid Error", filename)
|
||||
}
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
|
||||
// ApplyLayout applies the named layout to bytes.
|
||||
func (s *Site) ApplyLayout(name string, b []byte, e templates.VariableMap) ([]byte, error) {
|
||||
for name != "" {
|
||||
var lfm templates.VariableMap
|
||||
t, err := s.FindLayout(name, &lfm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
le := templates.MergeVariableMaps(e, templates.VariableMap{
|
||||
"content": string(b),
|
||||
"layout": lfm,
|
||||
})
|
||||
b, err = t.Render(le)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name = lfm.String("layout", "")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (s *Site) makeLocalLiquidEngine() liquid.Engine {
|
||||
engine := liquid.NewLocalWrapperEngine()
|
||||
engine.LinkTagHandler(s.RelPathURL)
|
||||
includeHandler := func(name string, w io.Writer, scope map[string]interface{}) error {
|
||||
filename := filepath.Join(s.Source, s.config.IncludesDir, name)
|
||||
template, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
text, err := engine.ParseAndRender(template, scope)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(text)
|
||||
return err
|
||||
}
|
||||
engine.IncludeHandler(includeHandler)
|
||||
return engine
|
||||
}
|
||||
|
||||
func (s *Site) makeLiquidClient() (engine liquid.RemoteEngine, err error) {
|
||||
engine, err = liquid.NewRPCClientEngine(liquid.DefaultServer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
urls := map[string]string{}
|
||||
for _, p := range s.Paths {
|
||||
urls[p.SiteRelPath()] = p.Permalink()
|
||||
}
|
||||
err = engine.FileURLMap(urls)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = engine.IncludeDirs([]string{filepath.Join(s.Source, s.config.IncludesDir)})
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Site) makeLiquidEngine() (liquid.Engine, error) {
|
||||
if s.UseRemoteLiquidEngine {
|
||||
return s.makeLiquidClient()
|
||||
}
|
||||
return s.makeLocalLiquidEngine(), nil
|
||||
}
|
||||
|
||||
// TemplateEngine creates a liquid engine configured to with include paths and link tag resolution
|
||||
// for this site.
|
||||
func (s *Site) TemplateEngine() liquid.Engine {
|
||||
return s.liquidEngine
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package sites
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -127,53 +126,3 @@ func (s *Site) Exclude(path string) bool {
|
||||
func (s *Site) LayoutsDir() string {
|
||||
return filepath.Join(s.Source, s.config.LayoutsDir)
|
||||
}
|
||||
|
||||
func (s *Site) makeLocalLiquidEngine() liquid.Engine {
|
||||
engine := liquid.NewLocalWrapperEngine()
|
||||
engine.LinkTagHandler(s.RelPathURL)
|
||||
includeHandler := func(name string, w io.Writer, scope map[string]interface{}) error {
|
||||
filename := filepath.Join(s.Source, s.config.IncludesDir, name)
|
||||
template, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
text, err := engine.ParseAndRender(template, scope)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(text)
|
||||
return err
|
||||
}
|
||||
engine.IncludeHandler(includeHandler)
|
||||
return engine
|
||||
}
|
||||
|
||||
func (s *Site) makeLiquidClient() (engine liquid.RemoteEngine, err error) {
|
||||
engine, err = liquid.NewRPCClientEngine(liquid.DefaultServer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
urls := map[string]string{}
|
||||
for _, p := range s.Paths {
|
||||
urls[p.SiteRelPath()] = p.Permalink()
|
||||
}
|
||||
err = engine.FileURLMap(urls)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = engine.IncludeDirs([]string{filepath.Join(s.Source, s.config.IncludesDir)})
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Site) makeLiquidEngine() (liquid.Engine, error) {
|
||||
if s.UseRemoteLiquidEngine {
|
||||
return s.makeLiquidClient()
|
||||
}
|
||||
return s.makeLocalLiquidEngine(), nil
|
||||
}
|
||||
|
||||
// TemplateEngine create a liquid engine configured to with include paths and link tag resolution
|
||||
// for this site.
|
||||
func (s *Site) TemplateEngine() liquid.Engine {
|
||||
return s.liquidEngine
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/osteele/gojekyll/helpers"
|
||||
"github.com/osteele/gojekyll/pages"
|
||||
"github.com/osteele/gojekyll/templates"
|
||||
)
|
||||
|
||||
@ -27,31 +26,24 @@ func (s *Site) initSiteVariables() error {
|
||||
"time": time.Now(),
|
||||
// TODO pages, posts, related_posts, static_files, html_pages, html_files, collections, data, documents, categories.CATEGORY, tags.TAG
|
||||
})
|
||||
s.updateCollectionVariables()
|
||||
return nil
|
||||
return s.updateCollectionVariables(false)
|
||||
}
|
||||
|
||||
// SetPageContentTemplateValues sets the site[collection][i].content
|
||||
// template variables
|
||||
func (s *Site) SetPageContentTemplateValues() error {
|
||||
for _, c := range s.Collections {
|
||||
for _, p := range c.Pages() {
|
||||
switch p := p.(type) {
|
||||
case *pages.DynamicPage:
|
||||
if err := p.ComputeContent(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s.updateCollectionVariables()
|
||||
return nil
|
||||
return s.updateCollectionVariables(true)
|
||||
}
|
||||
|
||||
func (s *Site) updateCollectionVariables() {
|
||||
func (s *Site) updateCollectionVariables(includeContent bool) error {
|
||||
for _, c := range s.Collections {
|
||||
s.Variables[c.Name] = c.TemplateVariable()
|
||||
v, err := c.TemplateVariable(s, includeContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Variables[c.Name] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Site) readDataFiles() (templates.VariableMap, error) {
|
||||
|
Loading…
Reference in New Issue
Block a user