Commit 7c986791 authored by Tobias Weingartner's avatar Tobias Weingartner

Fixup tests and move metadata into own package

parent 48d294a6
...@@ -70,9 +70,26 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error ...@@ -70,9 +70,26 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
continue continue
} }
var dirents []os.FileInfo
fpath := r.URL.Path fpath := r.URL.Path
if idx, ok := middleware.IndexFile(md.FileSys, fpath, md.IndexFiles); ok { if idx, ok := middleware.IndexFile(md.FileSys, fpath, md.IndexFiles); ok {
// We're serving a directory index file, which may be a markdown
// file with a template. Let's grab a list of files this directory
// URL points to, and pass that in to any possible template invocations,
// so that templates can customize the look and feel of a directory.
fdp, err := md.FileSys.Open(fpath)
if err != nil {
return http.StatusInternalServerError, err
}
dirents, err = fdp.Readdir(-1)
if err != nil {
return http.StatusInternalServerError, err
}
// Set path to found index file
fpath = idx fpath = idx
_ = dirents
} }
// If supported extension, process it // If supported extension, process it
...@@ -100,11 +117,16 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error ...@@ -100,11 +117,16 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
Req: r, Req: r,
URL: r.URL, URL: r.URL,
} }
html, err := md.Process(cfg, fpath, body, ctx) html, err := cfg.Markdown(fpath, body, ctx)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
} }
// html, err = md.doTemplate(cfg, html, ctx)
// if err != nil {
// return http.StatusInternalServerError, err
// }
middleware.SetLastModifiedHeader(w, fs.ModTime()) middleware.SetLastModifiedHeader(w, fs.ModTime())
w.Write(html) w.Write(html)
return http.StatusOK, nil return http.StatusOK, nil
......
package markdown package metadata
import ( import (
"bufio"
"bytes" "bytes"
"fmt" "fmt"
"time" "time"
...@@ -64,20 +65,23 @@ func (m *Metadata) load(parsedMap map[string]interface{}) { ...@@ -64,20 +65,23 @@ func (m *Metadata) load(parsedMap map[string]interface{}) {
// MetadataParser is a an interface that must be satisfied by each parser // MetadataParser is a an interface that must be satisfied by each parser
type MetadataParser interface { type MetadataParser interface {
// Initialize a parser
Init(b *bytes.Buffer) bool
// Type of metadata
Type() string
// Opening identifier // Opening identifier
Opening() []byte Opening() []byte
// Closing identifier // Closing identifier
Closing() []byte Closing() []byte
// Parse the metadata.
// Returns the remaining page contents (Markdown)
// after extracting metadata
Parse([]byte) ([]byte, error)
// Parsed metadata. // Parsed metadata.
// Should be called after a call to Parse returns no error
Metadata() Metadata Metadata() Metadata
// Raw markdown.
Markdown() []byte
} }
// extractMetadata separates metadata content from from markdown content in b. // extractMetadata separates metadata content from from markdown content in b.
...@@ -109,8 +113,19 @@ func extractMetadata(parser MetadataParser, b []byte) (metadata []byte, markdown ...@@ -109,8 +113,19 @@ func extractMetadata(parser MetadataParser, b []byte) (metadata []byte, markdown
return metadata, markdown, nil return metadata, markdown, nil
} }
func GetParser(buf []byte) MetadataParser {
for _, p := range parsers() {
b := bytes.NewBuffer(buf)
if p.Init(b) {
return p
}
}
return nil
}
// findParser finds the parser using line that contains opening identifier // findParser finds the parser using line that contains opening identifier
func findParser(b []byte) MetadataParser { func FindParser(b []byte) MetadataParser {
var line []byte var line []byte
// Read first line // Read first line
if _, err := fmt.Fscanln(bytes.NewReader(b), &line); err != nil { if _, err := fmt.Fscanln(bytes.NewReader(b), &line); err != nil {
...@@ -125,7 +140,7 @@ func findParser(b []byte) MetadataParser { ...@@ -125,7 +140,7 @@ func findParser(b []byte) MetadataParser {
return nil return nil
} }
func newMetadata() Metadata { func NewMetadata() Metadata {
return Metadata{ return Metadata{
Variables: make(map[string]string), Variables: make(map[string]string),
Flags: make(map[string]bool), Flags: make(map[string]bool),
...@@ -135,8 +150,53 @@ func newMetadata() Metadata { ...@@ -135,8 +150,53 @@ func newMetadata() Metadata {
// parsers returns all available parsers // parsers returns all available parsers
func parsers() []MetadataParser { func parsers() []MetadataParser {
return []MetadataParser{ return []MetadataParser{
&JSONMetadataParser{metadata: newMetadata()}, &TOMLMetadataParser{},
&TOMLMetadataParser{metadata: newMetadata()}, &YAMLMetadataParser{metadata: NewMetadata()},
&YAMLMetadataParser{metadata: newMetadata()}, &JSONMetadataParser{},
&NoneMetadataParser{},
}
}
// Split out "normal" metadata with given delimiter
func splitBuffer(b *bytes.Buffer, delim string) (*bytes.Buffer, *bytes.Buffer) {
scanner := bufio.NewScanner(b)
// Read and check first line
if !scanner.Scan() {
return nil, nil
}
if string(bytes.TrimSpace(scanner.Bytes())) != delim {
return nil, nil
}
// Accumulate metadata, until delimiter
meta := bytes.NewBuffer(nil)
for scanner.Scan() {
if string(bytes.TrimSpace(scanner.Bytes())) == delim {
break
}
if _, err := meta.Write(scanner.Bytes()); err != nil {
return nil, nil
}
if _, err := meta.WriteRune('\n'); err != nil {
return nil, nil
}
} }
// Make sure we saw closing delimiter
if string(bytes.TrimSpace(scanner.Bytes())) != delim {
return nil, nil
}
// The rest is markdown
markdown := new(bytes.Buffer)
for scanner.Scan() {
if _, err := markdown.Write(scanner.Bytes()); err != nil {
return nil, nil
}
if _, err := markdown.WriteRune('\n'); err != nil {
return nil, nil
}
}
return meta, markdown
} }
package markdown package metadata
import ( import (
"bytes" "bytes"
...@@ -8,6 +8,39 @@ import ( ...@@ -8,6 +8,39 @@ import (
// JSONMetadataParser is the MetadataParser for JSON // JSONMetadataParser is the MetadataParser for JSON
type JSONMetadataParser struct { type JSONMetadataParser struct {
metadata Metadata metadata Metadata
markdown *bytes.Buffer
}
func (j *JSONMetadataParser) Type() string {
return "JSON"
}
// Parse metadata/markdown file
func (j *JSONMetadataParser) Init(b *bytes.Buffer) bool {
m := make(map[string]interface{})
err := json.Unmarshal(b.Bytes(), &m)
if err != nil {
var offset int
if jerr, ok := err.(*json.SyntaxError); !ok {
return false
} else {
offset = int(jerr.Offset)
}
m = make(map[string]interface{})
err = json.Unmarshal(b.Next(offset-1), &m)
if err != nil {
return false
}
}
j.metadata = NewMetadata()
j.metadata.load(m)
j.markdown = bytes.NewBuffer(b.Bytes())
return true
} }
// Parse the metadata // Parse the metadata
...@@ -34,6 +67,10 @@ func (j *JSONMetadataParser) Metadata() Metadata { ...@@ -34,6 +67,10 @@ func (j *JSONMetadataParser) Metadata() Metadata {
return j.metadata return j.metadata
} }
func (j *JSONMetadataParser) Markdown() []byte {
return j.markdown.Bytes()
}
// Opening returns the opening identifier JSON metadata // Opening returns the opening identifier JSON metadata
func (j *JSONMetadataParser) Opening() []byte { func (j *JSONMetadataParser) Opening() []byte {
return []byte("{") return []byte("{")
......
package metadata
import (
"bytes"
)
// TOMLMetadataParser is the MetadataParser for TOML
type NoneMetadataParser struct {
metadata Metadata
markdown *bytes.Buffer
}
func (n *NoneMetadataParser) Type() string {
return "None"
}
// Parse metadata/markdown file
func (n *NoneMetadataParser) Init(b *bytes.Buffer) bool {
m := make(map[string]interface{})
n.metadata = NewMetadata()
n.metadata.load(m)
n.markdown = bytes.NewBuffer(b.Bytes())
return true
}
// Parse the metadata
func (n *NoneMetadataParser) Parse(b []byte) ([]byte, error) {
return nil, nil
}
// Metadata returns parsed metadata. It should be called
// only after a call to Parse returns without error.
func (n *NoneMetadataParser) Metadata() Metadata {
return n.metadata
}
func (n *NoneMetadataParser) Markdown() []byte {
return n.markdown.Bytes()
}
// Opening returns the opening identifier TOML metadata
func (n *NoneMetadataParser) Opening() []byte {
return []byte("...")
}
// Closing returns the closing identifier TOML metadata
func (n *NoneMetadataParser) Closing() []byte {
return []byte("...")
}
package markdown package metadata
import ( import (
"bytes" "bytes"
"fmt"
"reflect"
"strings" "strings"
"testing" "testing"
) )
...@@ -164,56 +162,52 @@ func TestParsers(t *testing.T) { ...@@ -164,56 +162,52 @@ func TestParsers(t *testing.T) {
testData [5]string testData [5]string
name string name string
}{ }{
{&JSONMetadataParser{metadata: newMetadata()}, JSON, "json"}, {&JSONMetadataParser{}, JSON, "JSON"},
{&YAMLMetadataParser{metadata: newMetadata()}, YAML, "yaml"}, {&YAMLMetadataParser{}, YAML, "YAML"},
{&TOMLMetadataParser{metadata: newMetadata()}, TOML, "toml"}, {&TOMLMetadataParser{}, TOML, "TOML"},
} }
for _, v := range data { for _, v := range data {
// metadata without identifiers // metadata without identifiers
if _, err := v.parser.Parse([]byte(v.testData[0])); err == nil { if v.parser.Init(bytes.NewBufferString(v.testData[0])) {
t.Fatalf("Expected error for invalid metadata for %v", v.name) t.Fatalf("Expected error for invalid metadata for %v", v.name)
} }
// metadata with identifiers // metadata with identifiers
md, err := v.parser.Parse([]byte(v.testData[1])) if !v.parser.Init(bytes.NewBufferString(v.testData[1])) {
check(t, err) t.Fatalf("Metadata failed to initialize, type %v", v.parser.Type())
}
md := v.parser.Markdown()
if !compare(v.parser.Metadata()) { if !compare(v.parser.Metadata()) {
t.Fatalf("Expected %v, found %v for %v", expected, v.parser.Metadata(), v.name) t.Fatalf("Expected %v, found %v for %v", expected, v.parser.Metadata(), v.name)
} }
if "Page content" != strings.TrimSpace(string(md)) { if "Page content" != strings.TrimSpace(string(md)) {
t.Fatalf("Expected %v, found %v for %v", "Page content", string(md), v.name) t.Fatalf("Expected %v, found %v for %v", "Page content", string(md), v.name)
} }
// Check that we find the correct metadata parser type
var line []byte if p := GetParser([]byte(v.testData[1])); p.Type() != v.name {
fmt.Fscanln(bytes.NewReader([]byte(v.testData[1])), &line) t.Fatalf("Wrong parser found, expected %v, found %v", v.name, p.Type())
if parser := findParser(line); parser == nil {
t.Fatalf("Parser must be found for %v", v.name)
} else {
if reflect.TypeOf(parser) != reflect.TypeOf(v.parser) {
t.Fatalf("parsers not equal. %v != %v", reflect.TypeOf(parser), reflect.TypeOf(v.parser))
}
} }
// metadata without closing identifier // metadata without closing identifier
if _, err := v.parser.Parse([]byte(v.testData[2])); err == nil { if v.parser.Init(bytes.NewBufferString(v.testData[2])) {
t.Fatalf("Expected error for missing closing identifier for %v", v.name) t.Fatalf("Expected error for missing closing identifier for %v parser", v.name)
} }
// invalid metadata // invalid metadata
if _, err = v.parser.Parse([]byte(v.testData[3])); err == nil { if v.parser.Init(bytes.NewBufferString(v.testData[3])) {
t.Fatalf("Expected error for invalid metadata for %v", v.name) t.Fatalf("Expected error for invalid metadata for %v", v.name)
} }
// front matter but no body // front matter but no body
if _, err = v.parser.Parse([]byte(v.testData[4])); err != nil { if !v.parser.Init(bytes.NewBufferString(v.testData[4])) {
t.Fatalf("Unexpected error for valid metadata but no body for %v", v.name) t.Fatalf("Unexpected error for valid metadata but no body for %v", v.name)
} }
} }
} }
func TestLargeBody(t *testing.T) { func TestLargeBody(t *testing.T) {
var JSON = `{ var JSON = `{
"template": "chapter" "template": "chapter"
} }
...@@ -232,24 +226,36 @@ Mycket olika byggnader har man i de nordiska rikena: pyramidformiga, kilformiga, ...@@ -232,24 +226,36 @@ Mycket olika byggnader har man i de nordiska rikena: pyramidformiga, kilformiga,
template : chapter template : chapter
--- ---
Mycket olika byggnader har man i de nordiska rikena: pyramidformiga, kilformiga, välvda, runda och fyrkantiga. De pyramidformiga består helt enkelt av träribbor, som upptill löper samman och nedtill bildar en vidare krets; de är avsedda att användas av hantverkarna under sommaren, för att de inte ska plågas av solen, på samma gång som de besväras av rök och eld. De kilformiga husen är i regel försedda med höga tak, för att de täta och tunga snömassorna fortare ska kunna blåsa av och inte tynga ned taken. Dessa är täckta av björknäver, tegel eller kluvet spån av furu - för kådans skull -, gran, ek eller bok; taken på de förmögnas hus däremot med plåtar av koppar eller bly, i likhet med kyrktaken. Valvbyggnaderna uppförs ganska konstnärligt till skydd mot våldsamma vindar och snöfall, görs av sten eller trä, och är avsedda för olika alldagliga viktiga ändamål. Liknande byggnader kan finnas i stormännens gårdar där de används som förvaringsrum för husgeråd och jordbruksredskap. De runda byggnaderna - som för övrigt är de högst sällsynta - används av konstnärer, som vid sitt arbete behöver ett jämnt fördelat ljus från taket. Vanligast är de fyrkantiga husen, vars grova bjälkar är synnerligen väl hopfogade i hörnen - ett sant mästerverk av byggnadskonst; även dessa har fönster högt uppe i taken, för att dagsljuset skall kunna strömma in och ge alla därinne full belysning. Stenhusen har dörröppningar i förhållande till byggnadens storlek, men smala fönstergluggar, som skydd mot den stränga kölden, frosten och snön. Vore de större och vidare, såsom fönstren i Italien, skulle husen i följd av den fint yrande snön, som röres upp av den starka blåsten, precis som dammet av virvelvinden, snart nog fyllas med massor av snö och inte kunna stå emot dess tryck, utan störta samman.
`
var NONE = `
Mycket olika byggnader har man i de nordiska rikena: pyramidformiga, kilformiga, välvda, runda och fyrkantiga. De pyramidformiga består helt enkelt av träribbor, som upptill löper samman och nedtill bildar en vidare krets; de är avsedda att användas av hantverkarna under sommaren, för att de inte ska plågas av solen, på samma gång som de besväras av rök och eld. De kilformiga husen är i regel försedda med höga tak, för att de täta och tunga snömassorna fortare ska kunna blåsa av och inte tynga ned taken. Dessa är täckta av björknäver, tegel eller kluvet spån av furu - för kådans skull -, gran, ek eller bok; taken på de förmögnas hus däremot med plåtar av koppar eller bly, i likhet med kyrktaken. Valvbyggnaderna uppförs ganska konstnärligt till skydd mot våldsamma vindar och snöfall, görs av sten eller trä, och är avsedda för olika alldagliga viktiga ändamål. Liknande byggnader kan finnas i stormännens gårdar där de används som förvaringsrum för husgeråd och jordbruksredskap. De runda byggnaderna - som för övrigt är de högst sällsynta - används av konstnärer, som vid sitt arbete behöver ett jämnt fördelat ljus från taket. Vanligast är de fyrkantiga husen, vars grova bjälkar är synnerligen väl hopfogade i hörnen - ett sant mästerverk av byggnadskonst; även dessa har fönster högt uppe i taken, för att dagsljuset skall kunna strömma in och ge alla därinne full belysning. Stenhusen har dörröppningar i förhållande till byggnadens storlek, men smala fönstergluggar, som skydd mot den stränga kölden, frosten och snön. Vore de större och vidare, såsom fönstren i Italien, skulle husen i följd av den fint yrande snön, som röres upp av den starka blåsten, precis som dammet av virvelvinden, snart nog fyllas med massor av snö och inte kunna stå emot dess tryck, utan störta samman. Mycket olika byggnader har man i de nordiska rikena: pyramidformiga, kilformiga, välvda, runda och fyrkantiga. De pyramidformiga består helt enkelt av träribbor, som upptill löper samman och nedtill bildar en vidare krets; de är avsedda att användas av hantverkarna under sommaren, för att de inte ska plågas av solen, på samma gång som de besväras av rök och eld. De kilformiga husen är i regel försedda med höga tak, för att de täta och tunga snömassorna fortare ska kunna blåsa av och inte tynga ned taken. Dessa är täckta av björknäver, tegel eller kluvet spån av furu - för kådans skull -, gran, ek eller bok; taken på de förmögnas hus däremot med plåtar av koppar eller bly, i likhet med kyrktaken. Valvbyggnaderna uppförs ganska konstnärligt till skydd mot våldsamma vindar och snöfall, görs av sten eller trä, och är avsedda för olika alldagliga viktiga ändamål. Liknande byggnader kan finnas i stormännens gårdar där de används som förvaringsrum för husgeråd och jordbruksredskap. De runda byggnaderna - som för övrigt är de högst sällsynta - används av konstnärer, som vid sitt arbete behöver ett jämnt fördelat ljus från taket. Vanligast är de fyrkantiga husen, vars grova bjälkar är synnerligen väl hopfogade i hörnen - ett sant mästerverk av byggnadskonst; även dessa har fönster högt uppe i taken, för att dagsljuset skall kunna strömma in och ge alla därinne full belysning. Stenhusen har dörröppningar i förhållande till byggnadens storlek, men smala fönstergluggar, som skydd mot den stränga kölden, frosten och snön. Vore de större och vidare, såsom fönstren i Italien, skulle husen i följd av den fint yrande snön, som röres upp av den starka blåsten, precis som dammet av virvelvinden, snart nog fyllas med massor av snö och inte kunna stå emot dess tryck, utan störta samman.
` `
var expectedBody = `Mycket olika byggnader har man i de nordiska rikena: pyramidformiga, kilformiga, välvda, runda och fyrkantiga. De pyramidformiga består helt enkelt av träribbor, som upptill löper samman och nedtill bildar en vidare krets; de är avsedda att användas av hantverkarna under sommaren, för att de inte ska plågas av solen, på samma gång som de besväras av rök och eld. De kilformiga husen är i regel försedda med höga tak, för att de täta och tunga snömassorna fortare ska kunna blåsa av och inte tynga ned taken. Dessa är täckta av björknäver, tegel eller kluvet spån av furu - för kådans skull -, gran, ek eller bok; taken på de förmögnas hus däremot med plåtar av koppar eller bly, i likhet med kyrktaken. Valvbyggnaderna uppförs ganska konstnärligt till skydd mot våldsamma vindar och snöfall, görs av sten eller trä, och är avsedda för olika alldagliga viktiga ändamål. Liknande byggnader kan finnas i stormännens gårdar där de används som förvaringsrum för husgeråd och jordbruksredskap. De runda byggnaderna - som för övrigt är de högst sällsynta - används av konstnärer, som vid sitt arbete behöver ett jämnt fördelat ljus från taket. Vanligast är de fyrkantiga husen, vars grova bjälkar är synnerligen väl hopfogade i hörnen - ett sant mästerverk av byggnadskonst; även dessa har fönster högt uppe i taken, för att dagsljuset skall kunna strömma in och ge alla därinne full belysning. Stenhusen har dörröppningar i förhållande till byggnadens storlek, men smala fönstergluggar, som skydd mot den stränga kölden, frosten och snön. Vore de större och vidare, såsom fönstren i Italien, skulle husen i följd av den fint yrande snön, som röres upp av den starka blåsten, precis som dammet av virvelvinden, snart nog fyllas med massor av snö och inte kunna stå emot dess tryck, utan störta samman. var expectedBody = `Mycket olika byggnader har man i de nordiska rikena: pyramidformiga, kilformiga, välvda, runda och fyrkantiga. De pyramidformiga består helt enkelt av träribbor, som upptill löper samman och nedtill bildar en vidare krets; de är avsedda att användas av hantverkarna under sommaren, för att de inte ska plågas av solen, på samma gång som de besväras av rök och eld. De kilformiga husen är i regel försedda med höga tak, för att de täta och tunga snömassorna fortare ska kunna blåsa av och inte tynga ned taken. Dessa är täckta av björknäver, tegel eller kluvet spån av furu - för kådans skull -, gran, ek eller bok; taken på de förmögnas hus däremot med plåtar av koppar eller bly, i likhet med kyrktaken. Valvbyggnaderna uppförs ganska konstnärligt till skydd mot våldsamma vindar och snöfall, görs av sten eller trä, och är avsedda för olika alldagliga viktiga ändamål. Liknande byggnader kan finnas i stormännens gårdar där de används som förvaringsrum för husgeråd och jordbruksredskap. De runda byggnaderna - som för övrigt är de högst sällsynta - används av konstnärer, som vid sitt arbete behöver ett jämnt fördelat ljus från taket. Vanligast är de fyrkantiga husen, vars grova bjälkar är synnerligen väl hopfogade i hörnen - ett sant mästerverk av byggnadskonst; även dessa har fönster högt uppe i taken, för att dagsljuset skall kunna strömma in och ge alla därinne full belysning. Stenhusen har dörröppningar i förhållande till byggnadens storlek, men smala fönstergluggar, som skydd mot den stränga kölden, frosten och snön. Vore de större och vidare, såsom fönstren i Italien, skulle husen i följd av den fint yrande snön, som röres upp av den starka blåsten, precis som dammet av virvelvinden, snart nog fyllas med massor av snö och inte kunna stå emot dess tryck, utan störta samman.
` `
data := []struct { data := []struct {
parser MetadataParser pType string
testData string testData string
name string
}{ }{
{&JSONMetadataParser{metadata: newMetadata()}, JSON, "json"}, {"JSON", JSON},
{&YAMLMetadataParser{metadata: newMetadata()}, YAML, "yaml"}, {"TOML", TOML},
{&TOMLMetadataParser{metadata: newMetadata()}, TOML, "toml"}, {"YAML", YAML},
{"None", NONE},
} }
for _, v := range data { for _, v := range data {
// metadata without identifiers p := GetParser([]byte(v.testData))
if md, err := v.parser.Parse([]byte(v.testData)); err != nil || strings.TrimSpace(string(md)) != strings.TrimSpace(expectedBody) { if v.pType != p.Type() {
t.Fatalf("Error not expected and/or markdown not equal for %v", v.name) t.Fatalf("Wrong parser type, expected %v, got %v", v.pType, p.Type())
}
md := p.Markdown()
if strings.TrimSpace(string(md)) != strings.TrimSpace(expectedBody) {
t.Log("Provided:", v.testData)
t.Log("Returned:", p.Markdown())
t.Fatalf("Error, mismatched body in expected type %v, matched type %v", v.pType, p.Type())
} }
} }
} }
package markdown package metadata
import ( import (
"bytes"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
) )
// TOMLMetadataParser is the MetadataParser for TOML // TOMLMetadataParser is the MetadataParser for TOML
type TOMLMetadataParser struct { type TOMLMetadataParser struct {
metadata Metadata metadata Metadata
markdown *bytes.Buffer
}
func (t *TOMLMetadataParser) Type() string {
return "TOML"
}
// Parse metadata/markdown file
func (t *TOMLMetadataParser) Init(b *bytes.Buffer) bool {
meta, data := splitBuffer(b, "+++")
if meta == nil || data == nil {
return false
}
t.markdown = data
m := make(map[string]interface{})
if err := toml.Unmarshal(meta.Bytes(), &m); err != nil {
return false
}
t.metadata = NewMetadata()
t.metadata.load(m)
return true
} }
// Parse the metadata // Parse the metadata
...@@ -15,6 +40,7 @@ func (t *TOMLMetadataParser) Parse(b []byte) ([]byte, error) { ...@@ -15,6 +40,7 @@ func (t *TOMLMetadataParser) Parse(b []byte) ([]byte, error) {
if err != nil { if err != nil {
return markdown, err return markdown, err
} }
m := make(map[string]interface{}) m := make(map[string]interface{})
if err := toml.Unmarshal(b, &m); err != nil { if err := toml.Unmarshal(b, &m); err != nil {
return markdown, err return markdown, err
...@@ -29,6 +55,10 @@ func (t *TOMLMetadataParser) Metadata() Metadata { ...@@ -29,6 +55,10 @@ func (t *TOMLMetadataParser) Metadata() Metadata {
return t.metadata return t.metadata
} }
func (t *TOMLMetadataParser) Markdown() []byte {
return t.markdown.Bytes()
}
// Opening returns the opening identifier TOML metadata // Opening returns the opening identifier TOML metadata
func (t *TOMLMetadataParser) Opening() []byte { func (t *TOMLMetadataParser) Opening() []byte {
return []byte("+++") return []byte("+++")
......
package markdown package metadata
import ( import (
"bytes"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
// YAMLMetadataParser is the MetadataParser for YAML // YAMLMetadataParser is the MetadataParser for YAML
type YAMLMetadataParser struct { type YAMLMetadataParser struct {
metadata Metadata metadata Metadata
markdown *bytes.Buffer
}
func (y *YAMLMetadataParser) Type() string {
return "YAML"
}
func (y *YAMLMetadataParser) Init(b *bytes.Buffer) bool {
meta, data := splitBuffer(b, "---")
if meta == nil || data == nil {
return false
}
y.markdown = data
m := make(map[string]interface{})
if err := yaml.Unmarshal(meta.Bytes(), &m); err != nil {
return false
}
y.metadata = NewMetadata()
y.metadata.load(m)
return true
} }
// Parse the metadata // Parse the metadata
...@@ -30,6 +54,10 @@ func (y *YAMLMetadataParser) Metadata() Metadata { ...@@ -30,6 +54,10 @@ func (y *YAMLMetadataParser) Metadata() Metadata {
return y.metadata return y.metadata
} }
func (y *YAMLMetadataParser) Markdown() []byte {
return y.markdown.Bytes()
}
// Opening returns the opening identifier YAML metadata // Opening returns the opening identifier YAML metadata
func (y *YAMLMetadataParser) Opening() []byte { func (y *YAMLMetadataParser) Opening() []byte {
return []byte("---") return []byte("---")
......
package markdown package markdown
import ( import (
"bytes"
"path/filepath" "path/filepath"
"github.com/mholt/caddy/middleware" "github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/middleware/markdown/metadata"
"github.com/russross/blackfriday" "github.com/russross/blackfriday"
) )
// Data represents a markdown document. // Markdown processes the contents of a page in b. It parses the metadata
type Data struct {
middleware.Context
Doc map[string]string
DocFlags map[string]bool
Styles []string
Scripts []string
}
// Include "overrides" the embedded middleware.Context's Include()
// method so that included files have access to d's fields.
func (d Data) Include(filename string) (string, error) {
return middleware.ContextInclude(filename, d, d.Root)
}
// Process processes the contents of a page in b. It parses the metadata
// (if any) and uses the template (if found). // (if any) and uses the template (if found).
func (md Markdown) Process(c *Config, requestPath string, b []byte, ctx middleware.Context) ([]byte, error) { func (c *Config) Markdown(requestPath string, b []byte, ctx middleware.Context) ([]byte, error) {
var metadata = newMetadata() parser := metadata.GetParser(b)
var markdown []byte markdown := parser.Markdown()
var err error mdata := parser.Metadata()
// find parser compatible with page contents
parser := findParser(b)
if parser == nil {
// if not found, assume whole file is markdown (no front matter)
markdown = b
} else {
// if found, assume metadata present and parse.
markdown, err = parser.Parse(b)
if err != nil {
return nil, err
}
metadata = parser.Metadata()
}
// process markdown // process markdown
extns := 0 extns := 0
...@@ -54,33 +24,14 @@ func (md Markdown) Process(c *Config, requestPath string, b []byte, ctx middlewa ...@@ -54,33 +24,14 @@ func (md Markdown) Process(c *Config, requestPath string, b []byte, ctx middlewa
markdown = blackfriday.Markdown(markdown, c.Renderer, extns) markdown = blackfriday.Markdown(markdown, c.Renderer, extns)
// set it as body for template // set it as body for template
metadata.Variables["body"] = string(markdown) mdata.Variables["body"] = string(markdown)
title := metadata.Title title := mdata.Title
if title == "" { if title == "" {
title = filepath.Base(requestPath) title = filepath.Base(requestPath)
var extension = filepath.Ext(requestPath) var extension = filepath.Ext(requestPath)
title = title[0 : len(title)-len(extension)] title = title[0 : len(title)-len(extension)]
} }
metadata.Variables["title"] = title mdata.Variables["title"] = title
// return md.processTemplate(c, requestPath, metadata, ctx)
return md.doTemplate(c, requestPath, metadata, ctx)
}
// doTemplate executes a template given a requestPath, template, and metadata
func (md Markdown) doTemplate(c *Config, reqPath string, metadata Metadata, ctx middleware.Context) ([]byte, error) {
mdData := Data{
Context: ctx,
Doc: metadata.Variables,
DocFlags: metadata.Flags,
Styles: c.Styles,
Scripts: c.Scripts,
}
b := new(bytes.Buffer)
if err := c.Template.ExecuteTemplate(b, metadata.Template, mdData); err != nil {
return nil, err
}
return b.Bytes(), nil return execTemplate(c, mdata, ctx)
} }
package markdown package markdown
import ( import (
"bytes"
"io/ioutil" "io/ioutil"
"os"
"text/template" "text/template"
"github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/middleware/markdown/metadata"
) )
// Data represents a markdown document.
type Data struct {
middleware.Context
Doc map[string]string
DocFlags map[string]bool
Styles []string
Scripts []string
Files []os.FileInfo
}
// Include "overrides" the embedded middleware.Context's Include()
// method so that included files have access to d's fields.
// Note: using {{template 'template-name' .}} instead might be better.
func (d Data) Include(filename string) (string, error) {
return middleware.ContextInclude(filename, d, d.Root)
}
// execTemplate executes a template given a requestPath, template, and metadata
func execTemplate(c *Config, mdata metadata.Metadata, ctx middleware.Context) ([]byte, error) {
mdData := Data{
Context: ctx,
Doc: mdata.Variables,
DocFlags: mdata.Flags,
Styles: c.Styles,
Scripts: c.Scripts,
}
b := new(bytes.Buffer)
if err := c.Template.ExecuteTemplate(b, mdata.Template, mdData); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func setDefaultTemplate(filename string) *template.Template { func setDefaultTemplate(filename string) *template.Template {
buf, err := ioutil.ReadFile(filename) buf, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment