Commit 48a12c60 authored by Abiola Ibrahim's avatar Abiola Ibrahim

markdown: Added template support.

parent 0fccd370
package markdown
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"github.com/BurntSushi/toml"
"gopkg.in/yaml.v2"
......@@ -79,12 +82,12 @@ func (j *JSONMetadataParser) Metadata() Metadata {
// Opening returns the opening identifier JSON metadata
func (j *JSONMetadataParser) Opening() []byte {
return []byte("{")
return []byte(":::")
}
// Closing returns the closing identifier JSON metadata
func (j *JSONMetadataParser) Closing() []byte {
return []byte("}")
return []byte(":::")
}
// TOMLMetadataParser is the MetadataParser for TOML
......@@ -148,3 +151,65 @@ func (y *YAMLMetadataParser) Opening() []byte {
func (y *YAMLMetadataParser) Closing() []byte {
return []byte("---")
}
// extractMetadata extracts metadata content from a page.
// it returns the metadata, the remaining bytes (markdown),
// and an error if any
func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) {
b = bytes.TrimSpace(b)
reader := bytes.NewBuffer(b)
scanner := bufio.NewScanner(reader)
var parser MetadataParser
// Read first line
if scanner.Scan() {
line := scanner.Bytes()
parser = findParser(line)
// if no parser found,
// assume metadata not present
if parser == nil {
return metadata, b, nil
}
}
// buffer for metadata contents
buf := bytes.Buffer{}
// Read remaining lines until closing identifier is found
for scanner.Scan() {
line := scanner.Bytes()
// if closing identifier found
if bytes.Equal(bytes.TrimSpace(line), parser.Closing()) {
// parse the metadata
err := parser.Parse(buf.Bytes())
if err != nil {
return metadata, nil, err
}
// get the scanner to return remaining bytes
scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) {
return len(data), data, nil
})
// scan the remaining bytes
scanner.Scan()
return parser.Metadata(), scanner.Bytes(), nil
}
buf.Write(line)
buf.WriteString("\r\n")
}
// closing identifier not found
return metadata, nil, fmt.Errorf("Metadata not closed. '%v' not found", string(parser.Closing()))
}
// findParser finds the parser using line that contains opening identifier
func findParser(line []byte) MetadataParser {
line = bytes.TrimSpace(line)
for _, parser := range parsers {
if bytes.Equal(parser.Opening(), line) {
return parser
}
}
return nil
}
package markdown
import (
"bytes"
"fmt"
"reflect"
"testing"
)
var TOML = [4]string{`
title = "A title"
template = "default"
[variables]
name = "value"
`,
`+++
title = "A title"
template = "default"
[variables]
name = "value"
+++
`,
`+++
title = "A title"
template = "default"
[variables]
name = "value"
`,
`title = "A title" template = "default" [variables] name = "value"`,
}
var YAML = [4]string{`
title : A title
template : default
variables :
- name : value
`,
`---
title : A title
template : default
variables :
- name : value
---
`,
`---
title : A title
template : default
variables :
- name : value
`,
`title : A title template : default variables : name : value`,
}
var JSON = [4]string{`
{
"title" : "A title",
"template" : "default",
"variables" : {
"name" : "value"
}
}
`,
`:::
{
"title" : "A title",
"template" : "default",
"variables" : {
"name" : "value"
}
}
:::`,
`:::
{
"title" : "A title",
"template" : "default",
"variables" : {
"name" : "value"
}
}
`,
`
:::
{{
"title" : "A title",
"template" : "default",
"variables" : {
"name" : "value"
}
}
:::
`,
}
func check(t *testing.T, err error) {
if err != nil {
t.Fatal(err)
}
}
func TestParsers(t *testing.T) {
expected := Metadata{
Title: "A title",
Template: "default",
Variables: map[string]interface{}{"name": "value"},
}
compare := func(m Metadata) bool {
if m.Title != expected.Title {
return false
}
if m.Template != expected.Template {
return false
}
for k, v := range m.Variables {
if v != expected.Variables[k] {
return false
}
}
return true
}
data := []struct {
parser MetadataParser
testData [4]string
name string
}{
{&JSONMetadataParser{}, JSON, "json"},
{&YAMLMetadataParser{}, YAML, "yaml"},
{&TOMLMetadataParser{}, TOML, "toml"},
}
for _, v := range data {
// metadata without identifiers
err := v.parser.Parse([]byte(v.testData[0]))
check(t, err)
if !compare(v.parser.Metadata()) {
t.Fatalf("Expected %v, found %v for %v", expected, v.parser.Metadata().Variables, v.name)
}
// metadata with identifiers
metadata, _, err := extractMetadata([]byte(v.testData[1]))
check(t, err)
if !compare(metadata) {
t.Fatalf("Expected %v, found %v for %v", expected, metadata.Variables, v.name)
}
var line []byte
fmt.Fscanln(bytes.NewReader([]byte(v.testData[1])), &line)
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
if _, _, err := extractMetadata([]byte(v.testData[2])); err == nil {
t.Fatalf("Expected error for missing closing identifier for %v", v.name)
}
// invalid metadata
if err := v.parser.Parse([]byte(v.testData[3])); err == nil {
t.Fatalf("Expected error for invalid metadata for %v", v.name)
}
}
}
package markdown
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"text/template"
"github.com/russross/blackfriday"
"log"
"os"
"strings"
)
const (
......@@ -48,73 +47,11 @@ func (md Markdown) process(c Config, requestPath string, b []byte) ([]byte, erro
markdown = blackfriday.Markdown(markdown, c.Renderer, 0)
// set it as body for template
metadata.Variables["body"] = string(markdown)
metadata.Variables["markdown"] = string(markdown)
return md.processTemplate(c, requestPath, tmpl, metadata)
}
// extractMetadata extracts metadata content from a page.
// it returns the metadata, the remaining bytes (markdown),
// and an error if any
func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) {
b = bytes.TrimSpace(b)
reader := bytes.NewBuffer(b)
scanner := bufio.NewScanner(reader)
var parser MetadataParser
// Read first line
if scanner.Scan() {
line := scanner.Bytes()
parser = findParser(line)
// if no parser found,
// assume metadata not present
if parser == nil {
return metadata, b, nil
}
}
// buffer for metadata contents
buf := bytes.Buffer{}
// Read remaining lines until closing identifier is found
for scanner.Scan() {
line := scanner.Bytes()
// if closing identifier found
if bytes.Equal(bytes.TrimSpace(line), parser.Closing()) {
// parse the metadata
err := parser.Parse(buf.Bytes())
if err != nil {
return metadata, nil, err
}
// get the scanner to return remaining bytes
scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) {
return len(data), data, nil
})
// scan the remaining bytes
scanner.Scan()
return parser.Metadata(), scanner.Bytes(), nil
}
buf.Write(line)
buf.WriteString("\r\n")
}
// closing identifier not found
return metadata, nil, fmt.Errorf("Metadata not closed. '%v' not found", string(parser.Closing()))
}
// findParser finds the parser using line that contains opening identifier
func findParser(line []byte) MetadataParser {
line = bytes.TrimSpace(line)
for _, parser := range parsers {
if bytes.Equal(parser.Opening(), line) {
return parser
}
}
return nil
}
// processTemplate processes a template given a requestPath,
// template (tmpl) and metadata
func (md Markdown) processTemplate(c Config, requestPath string, tmpl []byte, metadata Metadata) ([]byte, error) {
......
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