mirror of
https://github.com/danog/gojekyll.git
synced 2024-11-26 23:34:47 +01:00
Use drops for site
This commit is contained in:
parent
257862fb3d
commit
fbd08abe3f
@ -109,7 +109,7 @@ func pageFromPathOrRoute(s *sites.Site, path string) (pages.Document, error) {
|
||||
|
||||
func varsCommand(site *sites.Site) error {
|
||||
printSetting("Variables:", "")
|
||||
siteData := site.SiteVariables()
|
||||
siteData := site.ToLiquid().(map[string]interface{})
|
||||
// The YAML representation including collections is impractically large for debugging.
|
||||
// Neuter it. This destroys it as Liquid data, but that's okay in this context.
|
||||
// for _, c := range site.Collections {
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/osteele/gojekyll/config"
|
||||
"github.com/osteele/gojekyll/constants"
|
||||
"github.com/osteele/gojekyll/pages"
|
||||
"github.com/osteele/gojekyll/templates"
|
||||
)
|
||||
@ -88,11 +87,8 @@ func (c *Collection) ToLiquid() interface{} {
|
||||
})
|
||||
}
|
||||
|
||||
// PermalinkPattern returns the permalink pattern for this collection.
|
||||
// PermalinkPattern returns the default permalink pattern for this collection.
|
||||
func (c *Collection) PermalinkPattern() string {
|
||||
defaultPattern := constants.DefaultCollectionPermalinkPattern
|
||||
if c.IsPostsCollection() {
|
||||
defaultPattern = constants.DefaultPostsCollectionPermalinkPattern
|
||||
}
|
||||
defaultPattern := c.strategy().defaultPermalinkPattern()
|
||||
return templates.VariableMap(c.Metadata).String("permalink", defaultPattern)
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package collections
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/osteele/gojekyll/helpers"
|
||||
)
|
||||
|
||||
// A collectionStrategy encapsulates behavior differences between the _post
|
||||
@ -10,6 +11,7 @@ import (
|
||||
type collectionStrategy interface {
|
||||
addDate(filename string, fm map[string]interface{})
|
||||
collectible(filename string) bool
|
||||
defaultPermalinkPattern() string
|
||||
future(filename string) bool
|
||||
}
|
||||
|
||||
@ -29,25 +31,31 @@ func (s defaultStrategy) future(filename string) bool { return fa
|
||||
type postsStrategy struct{}
|
||||
|
||||
func (s postsStrategy) addDate(filename string, fm map[string]interface{}) {
|
||||
if t, found := DateFromFilename(filename); found {
|
||||
if t, found := helpers.FilenameDate(filename); found {
|
||||
fm["date"] = t
|
||||
}
|
||||
}
|
||||
|
||||
func (s postsStrategy) collectible(filename string) bool {
|
||||
_, ok := DateFromFilename(filename)
|
||||
_, ok := helpers.FilenameDate(filename)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (s postsStrategy) future(filename string) bool {
|
||||
t, ok := DateFromFilename(filename)
|
||||
t, ok := helpers.FilenameDate(filename)
|
||||
return ok && t.After(time.Now())
|
||||
}
|
||||
|
||||
// DateFromFilename returns the date for a filename that uses Jekyll post convention.
|
||||
// It also returns a bool indicating whether a date was found.
|
||||
func DateFromFilename(s string) (time.Time, bool) {
|
||||
layout := "2006-01-02-"
|
||||
t, err := time.Parse(layout, filepath.Base(s + layout)[:len(layout)])
|
||||
return t, err == nil
|
||||
// DefaultCollectionPermalinkPattern is the default permalink pattern for pages in the posts collection
|
||||
const DefaultCollectionPermalinkPattern = "/:collection/:path:output_ext"
|
||||
|
||||
// DefaultPostsCollectionPermalinkPattern is the default collection permalink pattern
|
||||
const DefaultPostsCollectionPermalinkPattern = "/:categories/:year/:month/:day/:title.html"
|
||||
|
||||
func (s defaultStrategy) defaultPermalinkPattern() string {
|
||||
return DefaultCollectionPermalinkPattern
|
||||
}
|
||||
|
||||
func (s postsStrategy) defaultPermalinkPattern() string {
|
||||
return DefaultPostsCollectionPermalinkPattern
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
package constants
|
||||
|
||||
// DefaultPermalinkPattern is the default permalink pattern for pages that aren't in a collection
|
||||
const DefaultPermalinkPattern = "/:path:output_ext"
|
||||
|
||||
// DefaultCollectionPermalinkPattern is the default permalink pattern for pages in the posts collection
|
||||
const DefaultCollectionPermalinkPattern = "/:collection/:path:output_ext"
|
||||
|
||||
// DefaultPostsCollectionPermalinkPattern is the default collection permalink pattern
|
||||
const DefaultPostsCollectionPermalinkPattern = "/:categories/:year/:month/:day/:title.html"
|
@ -4,8 +4,17 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FilenameDate returns the date for a filename that uses Jekyll post convention.
|
||||
// It also returns a bool indicating whether a date was found.
|
||||
func FilenameDate(s string) (time.Time, bool) {
|
||||
layout := "2006-01-02-"
|
||||
t, err := time.Parse(layout, filepath.Base(s + layout)[:len(layout)])
|
||||
return t, err == nil
|
||||
}
|
||||
|
||||
// MustRel is like filepath.Rel, but panics if the path cannot be relativized.
|
||||
func MustRel(basepath, targpath string) string {
|
||||
relpath, err := filepath.Rel(basepath, targpath)
|
||||
|
@ -2,10 +2,28 @@ package helpers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func timeMustParse(s string) time.Time {
|
||||
t, err := time.Parse(time.RFC3339, s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func TestFilenameDate(t *testing.T) {
|
||||
d, found := FilenameDate("2017-07-02-post.html")
|
||||
require.True(t, found)
|
||||
require.Equal(t, timeMustParse("2017-07-02T00:00:00Z"), d)
|
||||
|
||||
d, found = FilenameDate("not-post.html")
|
||||
require.False(t, found)
|
||||
}
|
||||
|
||||
func TestTrimExt(t *testing.T) {
|
||||
require.Equal(t, "/a/b", TrimExt("/a/b.c"))
|
||||
require.Equal(t, "/a/b", TrimExt("/a/b"))
|
||||
|
@ -2,6 +2,7 @@ package pages
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/osteele/gojekyll/pipelines"
|
||||
"github.com/osteele/liquid"
|
||||
@ -30,11 +31,23 @@ type Document interface {
|
||||
setPermalink() error
|
||||
}
|
||||
|
||||
// Page is a document with frontmatter.
|
||||
type Page interface {
|
||||
Document
|
||||
// Content asks a page to compute its content.
|
||||
// This has the side effect of causing the content to subsequently appear in the drop.
|
||||
Content(rc RenderingContext) ([]byte, error)
|
||||
// PostDate returns the date computed from the filename or frontmatter.
|
||||
// It is an uncaught error to call this on a page that is not a Post.
|
||||
// TODO Should posts have their own interface?
|
||||
PostDate() time.Time
|
||||
}
|
||||
|
||||
// RenderingContext provides context information for rendering.
|
||||
type RenderingContext interface {
|
||||
RenderingPipeline() pipelines.PipelineInterface
|
||||
// SiteVariables is the value of the "site" template variable.
|
||||
SiteVariables() map[string]interface{} // value of the "site" template variable
|
||||
// Site is the value of the "site" template variable.
|
||||
Site() interface{} // used as a drop in the rendering context
|
||||
}
|
||||
|
||||
// Container is the document container.
|
@ -13,13 +13,6 @@ import (
|
||||
"github.com/osteele/liquid/generics"
|
||||
)
|
||||
|
||||
// Page is a post or collection page.
|
||||
type Page interface {
|
||||
Document
|
||||
Content(rc RenderingContext) ([]byte, error)
|
||||
PostDate() time.Time
|
||||
}
|
||||
|
||||
type page struct {
|
||||
file
|
||||
raw []byte
|
||||
@ -110,7 +103,7 @@ func (p *page) MarshalYAML() (interface{}, error) {
|
||||
func (p *page) TemplateContext(rc RenderingContext) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"page": p,
|
||||
"site": rc.SiteVariables(),
|
||||
"site": rc.Site(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,13 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/osteele/gojekyll/constants"
|
||||
"github.com/osteele/gojekyll/helpers"
|
||||
"github.com/osteele/gojekyll/templates"
|
||||
)
|
||||
|
||||
// DefaultPermalinkPattern is the default permalink pattern for pages that aren't in a collection
|
||||
const DefaultPermalinkPattern = "/:path:output_ext"
|
||||
|
||||
// PermalinkStyles defines built-in styles from https://jekyllrb.com/docs/permalinks/#builtinpermalinkstyles
|
||||
var PermalinkStyles = map[string]string{
|
||||
"date": "/:categories/:year/:month/:day/:title.html",
|
||||
@ -64,7 +66,7 @@ func (f *file) permalinkVariables() map[string]string {
|
||||
}
|
||||
|
||||
func (f *file) computePermalink(vars map[string]string) (src string, err error) {
|
||||
pattern := templates.VariableMap(f.frontMatter).String("permalink", constants.DefaultPermalinkPattern)
|
||||
pattern := templates.VariableMap(f.frontMatter).String("permalink", DefaultPermalinkPattern)
|
||||
if p, found := PermalinkStyles[pattern]; found {
|
||||
pattern = p
|
||||
}
|
||||
|
50
sites/drop.go
Normal file
50
sites/drop.go
Normal file
@ -0,0 +1,50 @@
|
||||
package sites
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/osteele/gojekyll/templates"
|
||||
"github.com/osteele/liquid/generics"
|
||||
)
|
||||
|
||||
// ToLiquid returns the site variable for template evaluation.
|
||||
func (s *Site) ToLiquid() interface{} {
|
||||
if len(s.drop) == 0 {
|
||||
s.initializeDrop()
|
||||
}
|
||||
return s.drop
|
||||
}
|
||||
|
||||
// MarshalYAML is part of the yaml.Marshaler interface
|
||||
// The variables subcommand uses this.
|
||||
func (s *Site) MarshalYAML() (interface{}, error) {
|
||||
return s.ToLiquid(), nil
|
||||
}
|
||||
|
||||
func (s *Site) initializeDrop() {
|
||||
vars := templates.MergeVariableMaps(s.config.Variables, map[string]interface{}{
|
||||
"data": s.data,
|
||||
// "collections": s.computeCollections(), // generics.MustConvert(s.config.Collections, reflect.TypeOf([]interface{}{})),
|
||||
// TODO read time from _config, if it's available
|
||||
"time": time.Now(),
|
||||
// TODO pages, static_files, html_pages, html_files, documents, tags.TAG
|
||||
})
|
||||
collections := []interface{}{}
|
||||
for _, c := range s.Collections {
|
||||
vars[c.Name] = c.Pages()
|
||||
collections = append(collections, c.ToLiquid())
|
||||
}
|
||||
generics.SortByProperty(collections, "label", true)
|
||||
vars["collections"] = collections
|
||||
s.drop = vars
|
||||
s.setPostVariables()
|
||||
}
|
||||
|
||||
func (s *Site) setPageContent() error {
|
||||
for _, c := range s.Collections {
|
||||
if err := c.SetPageContent(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
46
sites/posts.go
Normal file
46
sites/posts.go
Normal file
@ -0,0 +1,46 @@
|
||||
package sites
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/osteele/gojekyll/collections"
|
||||
"github.com/osteele/gojekyll/pages"
|
||||
)
|
||||
|
||||
func (s *Site) findPostCollection() *collections.Collection {
|
||||
for _, c := range s.Collections {
|
||||
if c.Name == "posts" {
|
||||
return c
|
||||
}
|
||||
}
|
||||
panic(fmt.Errorf("no posts!"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Site) setPostVariables() {
|
||||
c := s.findPostCollection()
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
var (
|
||||
ps = c.Pages()
|
||||
related = ps
|
||||
categories = map[string][]pages.Page{}
|
||||
tags = map[string][]pages.Page{}
|
||||
)
|
||||
if len(related) > 10 {
|
||||
related = related[:10]
|
||||
}
|
||||
for _, p := range ps {
|
||||
for _, k := range p.Categories() {
|
||||
ps, found := categories[k]
|
||||
if !found {
|
||||
ps = []pages.Page{}
|
||||
}
|
||||
categories[k] = append(ps, p)
|
||||
}
|
||||
}
|
||||
s.drop["categories"] = categories
|
||||
s.drop["tags"] = tags
|
||||
s.drop["related_posts"] = related
|
||||
}
|
@ -27,7 +27,7 @@ type Site struct {
|
||||
pipeline *pipelines.Pipeline
|
||||
docs []pages.Document // all documents, whether or not they are output
|
||||
preparedToRender bool
|
||||
siteVariables map[string]interface{}
|
||||
drop map[string]interface{}
|
||||
}
|
||||
|
||||
// SourceDir returns the site source directory.
|
||||
@ -53,7 +53,7 @@ func (s *Site) OutputPages() []pages.Document {
|
||||
// Pages returns all the pages, output or not.
|
||||
func (s *Site) Pages() []pages.Document { return s.docs }
|
||||
|
||||
// AbsDir is in the page.Container interface.
|
||||
// AbsDir is in the collections.Site interface.
|
||||
func (s *Site) AbsDir() string {
|
||||
d, err := filepath.Abs(s.SourceDir())
|
||||
if err != nil {
|
||||
@ -67,6 +67,11 @@ func (s *Site) Config() config.Config {
|
||||
return s.config
|
||||
}
|
||||
|
||||
// Site is in the pages.RenderingContext interface.
|
||||
func (s *Site) Site() interface{} {
|
||||
return s
|
||||
}
|
||||
|
||||
// PathPrefix is in the page.Container interface.
|
||||
func (s *Site) PathPrefix() string { return "" }
|
||||
|
||||
@ -82,8 +87,8 @@ func NewSite(flags config.Flags) *Site {
|
||||
func (s *Site) SetAbsoluteURL(url string) {
|
||||
s.config.AbsoluteURL = url
|
||||
s.config.Variables["url"] = url
|
||||
if s.siteVariables != nil {
|
||||
s.siteVariables["url"] = url
|
||||
if s.drop != nil {
|
||||
s.drop["url"] = url
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,82 +0,0 @@
|
||||
package sites
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/osteele/gojekyll/pages"
|
||||
"github.com/osteele/gojekyll/templates"
|
||||
"github.com/osteele/liquid/generics"
|
||||
)
|
||||
|
||||
// SiteVariables returns the site variable for template evaluation.
|
||||
func (s *Site) SiteVariables() map[string]interface{} {
|
||||
if len(s.siteVariables) == 0 {
|
||||
if err := s.initializeSiteVariables(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return s.siteVariables
|
||||
}
|
||||
|
||||
func (s *Site) initializeSiteVariables() error {
|
||||
s.siteVariables = templates.MergeVariableMaps(s.config.Variables, map[string]interface{}{
|
||||
"data": s.data,
|
||||
// "collections": s.computeCollections(), // generics.MustConvert(s.config.Collections, reflect.TypeOf([]interface{}{})),
|
||||
// TODO read time from _config, if it's available
|
||||
"time": time.Now(),
|
||||
// TODO pages, static_files, html_pages, html_files, documents, tags.TAG
|
||||
})
|
||||
return s.setCollectionVariables(false)
|
||||
}
|
||||
|
||||
// set site[collection.name] for each collection.
|
||||
func (s *Site) setCollectionVariables(includeContent bool) error {
|
||||
vars := []interface{}{}
|
||||
if includeContent {
|
||||
s.setPageContent()
|
||||
}
|
||||
for _, c := range s.Collections {
|
||||
s.siteVariables[c.Name] = c.Pages()
|
||||
vars = append(vars, c.ToLiquid())
|
||||
}
|
||||
generics.SortByProperty(vars, "label", true)
|
||||
s.siteVariables["collections"] = vars
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Site) setPageContent() error {
|
||||
for _, c := range s.Collections {
|
||||
if err := c.SetPageContent(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (s *Site) setPostVariables(ps []pages.Page) {
|
||||
var (
|
||||
related = ps
|
||||
categories = map[string][]pages.Page{}
|
||||
tags = map[string][]pages.Page{}
|
||||
)
|
||||
if len(related) > 10 {
|
||||
related = related[:10]
|
||||
}
|
||||
for _, p := range ps {
|
||||
for _, k := range p.Categories() {
|
||||
ps, found := categories[k]
|
||||
if !found {
|
||||
ps = []pages.Page{}
|
||||
}
|
||||
categories[k] = append(ps, p)
|
||||
}
|
||||
}
|
||||
s.siteVariables["categories"] = categories
|
||||
s.siteVariables["tags"] = tags
|
||||
s.siteVariables["related_posts"] = related
|
||||
}
|
||||
|
||||
func (s *Site) setCollectionContent() error {
|
||||
return s.setCollectionVariables(true)
|
||||
}
|
@ -15,10 +15,7 @@ func (s *Site) prepareRendering() error {
|
||||
if err := s.initializeRenderingPipeline(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.initializeSiteVariables(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.setCollectionContent(); err != nil {
|
||||
if err := s.setPageContent(); err != nil {
|
||||
return err
|
||||
}
|
||||
s.preparedToRender = true
|
||||
|
Loading…
Reference in New Issue
Block a user