1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-11-27 02:44:39 +01:00

remove page.RenderContext

This commit is contained in:
Oliver Steele 2017-07-10 13:23:51 -04:00
parent c6f33c91c3
commit 908fc1d897
19 changed files with 91 additions and 95 deletions

View File

@ -5,6 +5,7 @@ import (
"github.com/osteele/gojekyll/config" "github.com/osteele/gojekyll/config"
"github.com/osteele/gojekyll/pages" "github.com/osteele/gojekyll/pages"
"github.com/osteele/gojekyll/pipelines"
"github.com/osteele/gojekyll/templates" "github.com/osteele/gojekyll/templates"
) )
@ -21,6 +22,7 @@ type Collection struct {
// Site is the interface a site provides to collections it contains. // Site is the interface a site provides to collections it contains.
type Site interface { type Site interface {
Config() *config.Config Config() *config.Config
RenderingPipeline() pipelines.PipelineInterface
OutputExt(pathname string) string OutputExt(pathname string) string
} }
@ -65,9 +67,9 @@ func (c *Collection) Pages() []pages.Page {
} }
// SetPageContent sets up the collection's pages' "content". // SetPageContent sets up the collection's pages' "content".
func (c *Collection) SetPageContent(ctx pages.RenderingContext) error { func (c *Collection) SetPageContent() error {
for _, p := range c.Pages() { for _, p := range c.Pages() {
_, err := p.Content(ctx) _, err := p.Content()
if err != nil { if err != nil {
return err return err
} }

View File

@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/osteele/gojekyll/config" "github.com/osteele/gojekyll/config"
"github.com/osteele/gojekyll/pipelines"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -11,7 +12,7 @@ type siteMock struct{ c config.Config }
func (c siteMock) Config() *config.Config { return &c.c } func (c siteMock) Config() *config.Config { return &c.c }
func (c siteMock) OutputExt(s string) string { return "" } func (c siteMock) OutputExt(s string) string { return "" }
func (c siteMock) Site() interface{} { return c } func (c siteMock) RenderingPipeline() pipelines.PipelineInterface { panic("unimplemented") }
func TestNewCollection(t *testing.T) { func TestNewCollection(t *testing.T) {
site := siteMock{config.Default()} site := siteMock{config.Default()}

View File

@ -69,7 +69,7 @@ func (c *Collection) readFile(abs string, rel string, fm map[string]interface{})
default: default:
strategy.addDate(rel, fm) strategy.addDate(rel, fm)
} }
f, err := pages.NewFile(abs, c, filepath.ToSlash(rel), fm) f, err := pages.NewFile(c.site, abs, filepath.ToSlash(rel), fm)
switch { switch {
case err != nil: case err != nil:
return err return err

View File

@ -10,8 +10,8 @@ import (
) )
var ( var (
frontMatterMatcher = regexp.MustCompile(`(?s)^---\n(.+?\n)---\n`) frontMatterMatcher = regexp.MustCompile(`(?s)^---\n(.+?\n)---\n+`)
emptyFontMatterMatcher = regexp.MustCompile(`(?s)^---\n+---\n`) emptyFontMatterMatcher = regexp.MustCompile(`(?s)^---\n+---\n+`)
) )
// Read reads the frontmatter from a document. It modifies srcPtr to point to the // Read reads the frontmatter from a document. It modifies srcPtr to point to the

View File

@ -43,24 +43,13 @@ func (p *page) ToLiquid() interface{} {
root = utils.TrimExt(p.relpath) root = utils.TrimExt(p.relpath)
base = filepath.Base(root) base = filepath.Base(root)
content = p.raw content = p.raw
excerpt string
) )
if p.content != nil { if p.content != nil {
content = *p.content content = *p.content
} }
content = bytes.TrimSpace(content)
if ei, ok := p.frontMatter["excerpt"]; ok {
excerpt = fmt.Sprint(ei)
} else {
pos := bytes.Index(content, []byte(p.container.Config().ExcerptSeparator))
if pos < 0 {
pos = len(content)
}
excerpt = string(content[:pos])
}
data := map[string]interface{}{ data := map[string]interface{}{
"content": string(content), "content": string(content),
"excerpt": excerpt, "excerpt": p.excerpt(),
"path": relpath, "path": relpath,
"url": p.Permalink(), "url": p.Permalink(),
// TODO output // TODO output
@ -101,6 +90,21 @@ func (p *page) ToLiquid() interface{} {
return data return data
} }
func (p *page) excerpt() string {
if ei, ok := p.frontMatter["excerpt"]; ok {
return fmt.Sprint(ei)
}
content := p.raw
if p.content != nil {
content = *p.content
}
pos := bytes.Index(content, []byte(p.site.Config().ExcerptSeparator))
if pos >= 0 {
return string(content[:pos])
}
return string(content)
}
// MarshalYAML is part of the yaml.Marshaler interface // MarshalYAML is part of the yaml.Marshaler interface
// The variables subcommand uses this. // The variables subcommand uses this.
func (f *file) MarshalYAML() (interface{}, error) { func (f *file) MarshalYAML() (interface{}, error) {

View File

@ -10,7 +10,7 @@ import (
func TestPage_ToLiquid(t *testing.T) { func TestPage_ToLiquid(t *testing.T) {
cfg := config.Default() cfg := config.Default()
page, err := NewFile("testdata/excerpt.md", containerFake{cfg, ""}, "excerpt.md", map[string]interface{}{}) page, err := NewFile(siteFake{t, cfg}, "testdata/excerpt.md", "excerpt.md", map[string]interface{}{})
require.NoError(t, err) require.NoError(t, err)
drop := page.(liquid.Drop).ToLiquid() drop := page.(liquid.Drop).ToLiquid()
excerpt := drop.(map[string]interface{})["excerpt"] excerpt := drop.(map[string]interface{})["excerpt"]

View File

@ -3,7 +3,6 @@ package pages
import ( import (
"fmt" "fmt"
"os" "os"
"reflect"
"time" "time"
"github.com/osteele/gojekyll/frontmatter" "github.com/osteele/gojekyll/frontmatter"
@ -12,7 +11,7 @@ import (
// file is embedded in StaticFile and page // file is embedded in StaticFile and page
type file struct { type file struct {
container Container site Site
filename string // target os filepath filename string // target os filepath
relpath string // slash-separated path relative to site or container source relpath string // slash-separated path relative to site or container source
outputExt string outputExt string
@ -22,7 +21,7 @@ type file struct {
} }
func (f *file) String() string { func (f *file) String() string {
return fmt.Sprintf("%s{Path=%v, Permalink=%v}", reflect.TypeOf(f).Name(), f.relpath, f.permalink) return fmt.Sprintf("%T{Path=%v, Permalink=%v}", f.relpath, f.permalink)
} }
func (f *file) OutputExt() string { return f.outputExt } func (f *file) OutputExt() string { return f.outputExt }
@ -31,8 +30,10 @@ func (f *file) Permalink() string { return f.permalink }
func (f *file) Published() bool { return templates.VariableMap(f.frontMatter).Bool("published", true) } func (f *file) Published() bool { return templates.VariableMap(f.frontMatter).Bool("published", true) }
func (f *file) SourcePath() string { return f.relpath } func (f *file) SourcePath() string { return f.relpath }
// NewFile creates a Post or StaticFile. // NewFile creates a Page or StaticFile.
func NewFile(filename string, c Container, relpath string, defaults map[string]interface{}) (Document, error) { //
// filename is the absolute filename. relpath is the path relative to the site or collection directory.
func NewFile(s Site, filename string, relpath string, defaults map[string]interface{}) (Document, error) {
fm, err := frontmatter.FileHasFrontMatter(filename) fm, err := frontmatter.FileHasFrontMatter(filename)
if err != nil { if err != nil {
return nil, err return nil, err
@ -43,12 +44,12 @@ func NewFile(filename string, c Container, relpath string, defaults map[string]i
} }
fields := file{ fields := file{
container: c, site: s,
filename: filename, filename: filename,
frontMatter: defaults, frontMatter: defaults,
fileModTime: info.ModTime(), fileModTime: info.ModTime(),
relpath: relpath, relpath: relpath,
outputExt: c.OutputExt(relpath), outputExt: s.OutputExt(relpath),
} }
if fm { if fm {
return makePage(filename, fields) return makePage(filename, fields)

View File

@ -1,25 +1,39 @@
package pages package pages
import ( import (
"io"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/osteele/gojekyll/config" "github.com/osteele/gojekyll/config"
"github.com/osteele/gojekyll/pipelines"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
type containerFake struct { type siteFake struct {
t *testing.T
cfg config.Config cfg config.Config
prefix string
} }
func (c containerFake) Config() *config.Config { return &c.cfg } func (s siteFake) Config() *config.Config { return &s.cfg }
func (c containerFake) PathPrefix() string { return c.prefix } func (s siteFake) RenderingPipeline() pipelines.PipelineInterface { return &pipelineFake{s.t} }
func (c containerFake) OutputExt(p string) string { return filepath.Ext(p) } func (s siteFake) OutputExt(p string) string { return filepath.Ext(p) }
func TestPageCategories(t *testing.T) { type pipelineFake struct{ t *testing.T }
c := containerFake{config.Default(), ""}
func (p pipelineFake) OutputExt(string) string { return ".html" }
func (p pipelineFake) ApplyLayout(layout string, src []byte, vars map[string]interface{}) ([]byte, error) {
require.Equal(p.t, "layout1", layout)
return nil, nil
}
func (p pipelineFake) Render(w io.Writer, src []byte, filename string, lineNo int, vars map[string]interface{}) ([]byte, error) {
require.Equal(p.t, "testdata/page_with_layout.md", filename)
return nil, nil
}
func TestFile_Categories(t *testing.T) {
s := siteFake{t, config.Default()}
fm := map[string]interface{}{"categories": "b a"} fm := map[string]interface{}{"categories": "b a"}
f := file{container: c, frontMatter: fm} f := file{site: s, frontMatter: fm}
require.Equal(t, []string{"a", "b"}, f.Categories()) require.Equal(t, []string{"a", "b"}, f.Categories())
} }

View File

@ -17,23 +17,15 @@ type Document interface {
// Output // Output
Published() bool Published() bool
Static() bool Static() bool
Write(io.Writer, RenderingContext) error Write(io.Writer) error
Categories() []string Categories() []string
Tags() []string Tags() []string
} }
// RenderingContext provides context information for rendering. // Site is the interface that the site provides to a page.
type RenderingContext interface { type Site interface {
RenderingPipeline() pipelines.PipelineInterface
// Site is the value of the "site" template variable.
Site() interface{} // used as a drop in the rendering context
}
// Container is the document container.
// It's either the Site or Collection that immediately contains the document.
type Container interface {
Config() *config.Config Config() *config.Config
RenderingPipeline() pipelines.PipelineInterface
OutputExt(pathname string) string OutputExt(pathname string) string
PathPrefix() string // PathPrefix is the relative prefix, "" for the site and "_coll/" for a collection
} }

View File

@ -17,7 +17,7 @@ type Page interface {
Document Document
// Content asks a page to compute its content. // Content asks a page to compute its content.
// This has the side effect of causing the content to subsequently appear in the drop. // This has the side effect of causing the content to subsequently appear in the drop.
Content(rc RenderingContext) ([]byte, error) Content() ([]byte, error)
SetContent(content []byte) SetContent(content []byte)
FrontMatter() map[string]interface{} FrontMatter() map[string]interface{}
// PostDate returns the date computed from the filename or frontmatter. // PostDate returns the date computed from the filename or frontmatter.
@ -59,10 +59,10 @@ func makePage(filename string, f file) (*page, error) {
} }
// TemplateContext returns the local variables for template evaluation // TemplateContext returns the local variables for template evaluation
func (p *page) TemplateContext(rc RenderingContext) map[string]interface{} { func (p *page) TemplateContext() map[string]interface{} {
return map[string]interface{}{ return map[string]interface{}{
"page": p, "page": p,
"site": rc.Site(), "site": p.site,
} }
} }
@ -87,15 +87,15 @@ func (p *page) PostDate() time.Time {
} }
// Write applies Liquid and Markdown, as appropriate. // Write applies Liquid and Markdown, as appropriate.
func (p *page) Write(w io.Writer, rc RenderingContext) error { func (p *page) Write(w io.Writer) error {
content, err := p.Content(rc) content, err := p.Content()
if err != nil { if err != nil {
return err return err
} }
layout, ok := p.frontMatter["layout"].(string) layout, ok := p.frontMatter["layout"].(string)
if ok && layout != "" { if ok && layout != "" {
rp := rc.RenderingPipeline() rp := p.site.RenderingPipeline()
content, err = rp.ApplyLayout(layout, content, p.TemplateContext(rc)) content, err = rp.ApplyLayout(layout, content, p.TemplateContext())
if err != nil { if err != nil {
return err return err
} }
@ -105,13 +105,13 @@ func (p *page) Write(w io.Writer, rc RenderingContext) error {
} }
// Content computes the page content. // Content computes the page content.
func (p *page) Content(rc RenderingContext) ([]byte, error) { func (p *page) Content() ([]byte, error) {
if p.content != nil { if p.content != nil {
return *p.content, nil return *p.content, nil
} }
rp := rc.RenderingPipeline() pipe := p.site.RenderingPipeline()
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
b, err := rp.Render(buf, p.raw, p.filename, p.firstLine, p.TemplateContext(rc)) b, err := pipe.Render(buf, p.raw, p.filename, p.firstLine, p.TemplateContext())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2,38 +2,22 @@ package pages
import ( import (
"bytes" "bytes"
"io"
"testing" "testing"
"github.com/osteele/gojekyll/config" "github.com/osteele/gojekyll/config"
"github.com/osteele/gojekyll/pipelines"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
type renderingContextFake struct { // func (c pipelineFake) RenderingPipeline() pipelines.PipelineInterface { return c }
t *testing.T // func (c pipelineFake) Config() config.Config { return c.cfg }
cfg config.Config // func (c pipelineFake) PathPrefix() string { return "." }
} // func (c pipelineFake) Site() interface{} { return nil }
func (c renderingContextFake) RenderingPipeline() pipelines.PipelineInterface { return c }
func (c renderingContextFake) Config() config.Config { return c.cfg }
func (c renderingContextFake) PathPrefix() string { return "." }
func (c renderingContextFake) OutputExt(string) string { return ".html" }
func (c renderingContextFake) Site() interface{} { return nil }
func (c renderingContextFake) ApplyLayout(layout string, src []byte, vars map[string]interface{}) ([]byte, error) {
require.Equal(c.t, "layout1", layout)
return nil, nil
}
func (c renderingContextFake) Render(w io.Writer, src []byte, filename string, lineNo int, vars map[string]interface{}) ([]byte, error) {
require.Equal(c.t, "testdata/page_with_layout.md", filename)
return nil, nil
}
func TestPageWrite(t *testing.T) { func TestPageWrite(t *testing.T) {
cfg := config.Default() cfg := config.Default()
p, err := NewFile("testdata/page_with_layout.md", containerFake{cfg, ""}, "page_with_layout.md", map[string]interface{}{}) p, err := NewFile(siteFake{t, cfg}, "testdata/page_with_layout.md", "page_with_layout.md", map[string]interface{}{})
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, p) require.NotNil(t, p)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
require.NoError(t, p.Write(buf, renderingContextFake{t, cfg})) require.NoError(t, p.Write(buf))
} }

View File

@ -41,7 +41,7 @@ var templateVariableMatcher = regexp.MustCompile(`:\w+\b`)
// See https://jekyllrb.com/docs/permalinks/#template-variables // See https://jekyllrb.com/docs/permalinks/#template-variables
func (f *file) permalinkVariables() map[string]string { func (f *file) permalinkVariables() map[string]string {
var ( var (
relpath = strings.TrimPrefix(f.relpath, f.container.PathPrefix()) relpath = f.relpath
root = utils.TrimExt(relpath) root = utils.TrimExt(relpath)
name = filepath.Base(root) name = filepath.Base(root)
fm = f.frontMatter fm = f.frontMatter

View File

@ -30,13 +30,13 @@ var tests = []pathTest{
} }
var collectionTests = []pathTest{ var collectionTests = []pathTest{
{"_c/a/b/c.d", "/prefix/:collection/post", "/prefix/c/post"}, {"/a/b/c.d", "/prefix/:collection/post", "/prefix/c/post"},
{"_c/a/b/c.d", "/prefix:path/post", "/prefix/a/b/c/post"}, {"/a/b/c.d", "/prefix:path/post", "/prefix/a/b/c/post"},
} }
func TestExpandPermalinkPattern(t *testing.T) { func TestExpandPermalinkPattern(t *testing.T) {
var ( var (
c = containerFake{config.Default(), ""} s = siteFake{t, config.Default()}
d = map[string]interface{}{ d = map[string]interface{}{
"categories": "b a", "categories": "b a",
} }
@ -49,7 +49,7 @@ func TestExpandPermalinkPattern(t *testing.T) {
case ".md", ".markdown": case ".md", ".markdown":
ext = ".html" ext = ".html"
} }
p := file{container: c, relpath: path, frontMatter: fm, outputExt: ext} p := file{site: s, relpath: path, frontMatter: fm, outputExt: ext}
t0, err := time.Parse(time.RFC3339, "2006-02-03T15:04:05Z") t0, err := time.Parse(time.RFC3339, "2006-02-03T15:04:05Z")
require.NoError(t, err) require.NoError(t, err)
p.fileModTime = t0 p.fileModTime = t0
@ -68,7 +68,7 @@ func TestExpandPermalinkPattern(t *testing.T) {
runTests(tests) runTests(tests)
c = containerFake{config.Default(), "_c/"} s = siteFake{t, config.Default()}
d["collection"] = "c" d["collection"] = "c"
runTests(collectionTests) runTests(collectionTests)

View File

@ -13,8 +13,7 @@ type StaticFile struct {
// Static is in the File interface. // Static is in the File interface.
func (p *StaticFile) Static() bool { return true } func (p *StaticFile) Static() bool { return true }
// Write returns a bool indicating that the page is a static page. func (p *StaticFile) Write(w io.Writer) error {
func (p *StaticFile) Write(w io.Writer, _ RenderingContext) error {
in, err := os.Open(p.filename) in, err := os.Open(p.filename)
if err != nil { if err != nil {
return err return err

View File

@ -5,7 +5,6 @@ import (
"html" "html"
"io" "io"
"github.com/osteele/gojekyll/pages"
"github.com/osteele/liquid" "github.com/osteele/liquid"
"github.com/osteele/liquid/render" "github.com/osteele/liquid/render"
) )
@ -79,7 +78,7 @@ func (d *feedDoc) Content() []byte {
return b return b
} }
func (d *feedDoc) Write(w io.Writer, c pages.RenderingContext) error { func (d *feedDoc) Write(w io.Writer) error {
_, err := w.Write(d.Content()) _, err := w.Write(d.Content())
return err return err
} }

View File

@ -76,7 +76,7 @@ func (d *redirectionDoc) Content() []byte {
return buf.Bytes() return buf.Bytes()
} }
func (d *redirectionDoc) Write(w io.Writer, c pages.RenderingContext) error { func (d *redirectionDoc) Write(w io.Writer) error {
return redirectTemplate.Execute(w, d) return redirectTemplate.Execute(w, d)
} }

View File

@ -51,7 +51,7 @@ func (s *Site) initializeDrop() {
func (s *Site) setPageContent() error { func (s *Site) setPageContent() error {
for _, c := range s.Collections { for _, c := range s.Collections {
if err := c.SetPageContent(s); err != nil { if err := c.SetPageContent(); err != nil {
return err return err
} }
} }

View File

@ -74,7 +74,7 @@ func (s *Site) readFiles() error {
return nil return nil
} }
defaultFrontmatter := s.config.GetFrontMatterDefaults("", relname) defaultFrontmatter := s.config.GetFrontMatterDefaults("", relname)
p, err := pages.NewFile(filename, s, filepath.ToSlash(relname), defaultFrontmatter) p, err := pages.NewFile(s, filename, filepath.ToSlash(relname), defaultFrontmatter)
if err != nil { if err != nil {
return utils.PathError(err, "read", filename) return utils.PathError(err, "read", filename)
} }

View File

@ -31,7 +31,7 @@ func (s *Site) WriteDocument(w io.Writer, p pages.Document) error {
return err return err
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
if err := p.Write(buf, s); err != nil { if err := p.Write(buf); err != nil {
return err return err
} }
c := buf.Bytes() c := buf.Bytes()