Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ m.Use(render.Renderer(render.Options{
Charset: "UTF-8", // Sets encoding for json and html content-types. Default is "UTF-8".
IndentJSON: true, // Output human readable JSON
HTMLContentType: "application/xhtml+xml", // Output XHTML content type instead of default "text/html"
Asset: Asset, // Asset loads and returns the asset for the given name.
AssetNames: AssetNames(), // AssetNames is a slice of the asset names (paths).
}))
// ...
~~~
Expand Down
75 changes: 46 additions & 29 deletions render.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ type Options struct {
PrefixJSON []byte
// Allows changing of output to XHTML instead of HTML. Default is "text/html"
HTMLContentType string
// Asset loads and returns the asset for the given name.
Asset func(string) ([]byte, error)
// AssetNames is a slice of the asset names (paths).
AssetNames []string
}

// HTMLOptions is a struct for overriding some rendering Options for specific HTML call
Expand Down Expand Up @@ -173,47 +177,60 @@ func compile(options Options) *template.Template {
// parse an initial template in case we don't have any
template.Must(t.Parse("Martini"))

filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
r, err := filepath.Rel(dir, path)
if err != nil {
return err
if options.Asset != nil && options.AssetNames != nil {
// Load templates from the assets (binary data).
for _, path := range options.AssetNames {
if strings.HasPrefix(path, dir) {
load(options, dir, path, t, options.Asset)
}
}
} else {
// Load templates from the files.
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
load(options, dir, path, t, ioutil.ReadFile)
return nil
})
}

ext := getExt(r)
return t
}

for _, extension := range options.Extensions {
if ext == extension {
func getExt(s string) string {
if strings.Index(s, ".") == -1 {
return ""
}
return "." + strings.Join(strings.Split(s, ".")[1:], ".")
}

buf, err := ioutil.ReadFile(path)
if err != nil {
panic(err)
}
func load(options Options, dir string, path string, t *template.Template, read func(string) ([]byte, error)) {
r, err := filepath.Rel(dir, path)
if err != nil {
panic(err)
}

name := (r[0 : len(r)-len(ext)])
tmpl := t.New(filepath.ToSlash(name))
ext := getExt(r)

// add our funcmaps
for _, funcs := range options.Funcs {
tmpl.Funcs(funcs)
}
for _, extension := range options.Extensions {
if ext == extension {

// Bomb out if parse fails. We don't want any silent server starts.
template.Must(tmpl.Funcs(helperFuncs).Parse(string(buf)))
break
buf, err := read(path)
if err != nil {
panic(err)
}
}

return nil
})
name := (r[0 : len(r)-len(ext)])
tmpl := t.New(filepath.ToSlash(name))

return t
}
// add our funcmaps
for _, funcs := range options.Funcs {
tmpl.Funcs(funcs)
}

func getExt(s string) string {
if strings.Index(s, ".") == -1 {
return ""
// Bomb out if parse fails. We don't want any silent server starts.
template.Must(tmpl.Funcs(helperFuncs).Parse(string(buf)))
break
}
}
return "." + strings.Join(strings.Split(s, ".")[1:], ".")
}

type renderer struct {
Expand Down
50 changes: 49 additions & 1 deletion render_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package render

import (
"fmt"
"html/template"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -56,7 +57,7 @@ func Test_Render_JSON_Prefix(t *testing.T) {

expect(t, res.Code, 300)
expect(t, res.Header().Get(ContentType), ContentJSON+"; charset=UTF-8")
expect(t, res.Body.String(), prefix + `{"one":"hello","two":"world"}`)
expect(t, res.Body.String(), prefix+`{"one":"hello","two":"world"}`)
}

func Test_Render_Indented_JSON(t *testing.T) {
Expand Down Expand Up @@ -478,6 +479,53 @@ func Test_GetExt(t *testing.T) {
expect(t, getExt("test.go.html"), ".go.html")
}

func Test_Load(t *testing.T) {
// Case when loading templates from the assets (binary data).
m := martini.Classic()
m.Use(Renderer(Options{
Directory: "fixtures/basic",
Asset: func(name string) ([]byte, error) {
if name == "fixtures/basic/hello.tmpl" {
return []byte("test"), nil
}
return []byte{}, fmt.Errorf("not found: %s", name)
},
AssetNames: []string{"fixtures/basic/hello.tmpl"},
}))

m.Get("/foobar", func(r Render) {
r.HTML(200, "hello", "jeremy")
})

res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foobar", nil)

m.ServeHTTP(res, req)

expect(t, res.Code, 200)
expect(t, res.Header().Get(ContentType), ContentHTML+"; charset=UTF-8")
expect(t, res.Body.String(), "test")

// Case when loading templates from the files.
m = martini.Classic()
m.Use(Renderer(Options{
Directory: "fixtures/basic",
}))

m.Get("/foobar", func(r Render) {
r.HTML(200, "hello", "jeremy")
})

res = httptest.NewRecorder()
req, _ = http.NewRequest("GET", "/foobar", nil)

m.ServeHTTP(res, req)

expect(t, res.Code, 200)
expect(t, res.Header().Get(ContentType), ContentHTML+"; charset=UTF-8")
expect(t, res.Body.String(), "<h1>Hello jeremy</h1>\n")
}

/* Test Helpers */
func expect(t *testing.T, a interface{}, b interface{}) {
if a != b {
Expand Down