github.com/rotblauer/buffalo@v0.7.1-0.20170112214545-7aa55ef80dd3/render/template.go (about) 1 package render 2 3 import ( 4 "bytes" 5 "fmt" 6 "html/template" 7 "io" 8 "path/filepath" 9 "strings" 10 11 "github.com/gobuffalo/velvet" 12 "github.com/pkg/errors" 13 "github.com/shurcooL/github_flavored_markdown" 14 ) 15 16 type templateRenderer struct { 17 *Engine 18 contentType string 19 names []string 20 } 21 22 func (s templateRenderer) ContentType() string { 23 return s.contentType 24 } 25 26 func (s *templateRenderer) Render(w io.Writer, data Data) error { 27 var yield template.HTML 28 var err error 29 for _, name := range s.names { 30 yield, err = s.execute(name, data.ToVelvet()) 31 if err != nil { 32 err = errors.Errorf("error rendering %s:\n%+v", name, err) 33 return err 34 } 35 data["yield"] = yield 36 } 37 _, err = w.Write([]byte(yield)) 38 if err != nil { 39 return errors.WithStack(err) 40 } 41 return nil 42 } 43 44 func (s *templateRenderer) execute(name string, data *velvet.Context) (template.HTML, error) { 45 source, err := s.source(name) 46 if err != nil { 47 return "", err 48 } 49 50 err = source.Helpers.Add("partial", func(name string, help velvet.HelperContext) (template.HTML, error) { 51 p, err := s.partial(name, help.Context) 52 if err != nil { 53 return template.HTML(fmt.Sprintf("<pre>%s: %s</pre>", name, err.Error())), err 54 } 55 return p, nil 56 }) 57 if err != nil { 58 return template.HTML(fmt.Sprintf("<pre>%s: %s</pre>", name, err.Error())), err 59 } 60 yield, err := source.Exec(data) 61 if err != nil { 62 return template.HTML(fmt.Sprintf("<pre>%s: %s</pre>", name, err.Error())), err 63 } 64 return template.HTML(yield), nil 65 } 66 67 func (s *templateRenderer) source(name string) (*velvet.Template, error) { 68 var t *velvet.Template 69 var ok bool 70 var err error 71 if s.CacheTemplates { 72 if t, ok = s.templateCache[name]; ok { 73 return t.Clone(), nil 74 } 75 } 76 b, err := s.Resolver().Read(filepath.Join(s.TemplatesPath, name)) 77 if err != nil { 78 return nil, errors.WithStack(fmt.Errorf("could not find template: %s", name)) 79 } 80 if strings.ToLower(filepath.Ext(name)) == ".md" { 81 b = github_flavored_markdown.Markdown(b) 82 // unescape quotes so raymond can parse the file correctly. 83 b = bytes.Replace(b, []byte("""), []byte("\""), -1) 84 } 85 source := string(b) 86 t, err = velvet.Parse(source) 87 if err != nil { 88 return t, errors.Errorf("Error parsing %s: %+v", name, errors.WithStack(err)) 89 } 90 91 err = t.Helpers.AddMany(s.Helpers) 92 if err != nil { 93 return nil, err 94 } 95 if s.CacheTemplates { 96 s.templateCache[name] = t 97 } 98 return t.Clone(), err 99 } 100 101 func (s *templateRenderer) partial(name string, data *velvet.Context) (template.HTML, error) { 102 d, f := filepath.Split(name) 103 name = filepath.Join(d, "_"+f) 104 return s.execute(name, data) 105 } 106 107 // Template renders the named files using the specified 108 // content type and the github.com/aymerick/raymond 109 // package for templating. If more than 1 file is provided 110 // the second file will be considered a "layout" file 111 // and the first file will be the "content" file which will 112 // be placed into the "layout" using "{{yield}}". 113 func Template(c string, names ...string) Renderer { 114 e := New(Options{}) 115 return e.Template(c, names...) 116 } 117 118 // Template renders the named files using the specified 119 // content type and the github.com/aymerick/raymond 120 // package for templating. If more than 1 file is provided 121 // the second file will be considered a "layout" file 122 // and the first file will be the "content" file which will 123 // be placed into the "layout" using "{{yield}}". 124 func (e *Engine) Template(c string, names ...string) Renderer { 125 return &templateRenderer{ 126 Engine: e, 127 contentType: c, 128 names: names, 129 } 130 }