diff --git a/README.md b/README.md index e63185e..177b11f 100644 --- a/README.md +++ b/README.md @@ -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). })) // ... ~~~ diff --git a/render.go b/render.go index b932983..2b8f137 100644 --- a/render.go +++ b/render.go @@ -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 @@ -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 { diff --git a/render_test.go b/render_test.go index 0ea459c..127526f 100644 --- a/render_test.go +++ b/render_test.go @@ -1,6 +1,7 @@ package render import ( + "fmt" "html/template" "net/http" "net/http/httptest" @@ -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) { @@ -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(), "