A Markdown Static Site in Go (Martini)

I had been playing with Go for a while. A bit of struggle in the beginning — the language design is genuinely different from anything I’d used before — but I love it, and I’ll definitely be using it for some upcoming projects.
It taste like a shiny Saturday morning at the beach with a cup of coffee and a cigarette.
The way I usually learn a new language is to port an existing simple piece of code I’ve written in another language. It tells me how the new language thinks. Here I ported a small Ruby/Sinatra app — a very simple static site engine driven by Markdown.
Project Structure
In your working dir at $GOPATH:
views/
├── master.html
└── homepage.md
main.go
File: main.go
package main
import (
"fmt"
"github.com/codegangsta/martini"
"github.com/codegangsta/martini-contrib/render"
"github.com/russross/blackfriday"
"html/template"
)
func parseMarkdown(args ...interface{}) template.HTML {
s := blackfriday.MarkdownCommon([]byte(fmt.Sprintf("%s", args...)))
return template.HTML(s)
}
func main() {
engine := martini.Classic()
engine.Use(render.Renderer(render.Options{
Directory: "views",
Layout: "master",
Funcs: []template.FuncMap{{"pmd": parseMarkdown}},
Extensions: []string{".md", ".html"},
Charset: "UTF-8",
}))
engine.Get("/", func(r render.Render) {
r.HTML(200, "homepage", map[string]interface{}{"Title": "Golangers"})
})
engine.Run()
}
Three external packages are doing the heavy lifting: martini (a small Sinatra/Express-ish web framework for Go), martini-contrib/render (the template renderer), and blackfriday (the Markdown parser). fmt and html/template are standard library.
How the Renderer Is Wired
martini.Classic() gives us static file serving and routing out of the box. We then bolt the rendering middleware on with options:
Directory— where to look up template files.Layout— the layout template.Extensions— which file extensions count as templates.Funcs— template functions; here we exposeparseMarkdownunder the aliaspmd.
parseMarkdown itself is trivial — template.HTML over Blackfriday’s output, so we don’t double-escape the rendered Markdown in the layout.
Endpoint Routing
engine.Get("/", func(r render.Render) {
r.HTML(200, "homepage", map[string]interface{}{"Title": "Golangers"})
})
Whenever a request hits /, the renderer looks for homepage in the views directory and returns HTTP 200 with HTML. The map of key/value pairs is the binding context for the template.
File: views/homepage.md
# Hello Gophers
We're pursuing the happy life of being programmers, may golang make it closer.
[@komang](http://twitter.com/komang)
A plain Markdown file. Nothing special.
File: views/master.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ .Title }}</title>
</head>
<body>
{{ yield|pmd }}
</body>
</html>
{{ .Title }} is replaced with the Title value passed from the route. yield is the rendered template body (homepage.md); piping it through pmd runs it through parseMarkdown so the Markdown becomes HTML.
Compile and Run
Resolve dependencies first:
go get .
For development I like gin for live reload:
go get github.com/codegangsta/gin
$GOPATH/bin/gin main.go
Then visit http://127.0.0.1:3000/.
Code Repository
Code lives on GitHub:
go get github.com/chazuka/go/markdowner
cd $GOPATH/src/github.com/chazuka/go/markdowner
$GOPATH/bin/gin main.go
Suksma.
