1 Star 0 Fork 80

panrongcai/faygo

forked from andeyalee/faygo 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
render_pongo2.go 4.88 KB
一键复制 编辑 原始数据 按行查看 历史
henrylee2cn 提交于 2017-07-15 18:33 . fix template render
package faygo
import (
"bytes"
"errors"
"io/ioutil"
"net/http"
"os"
"sync"
"time"
"github.com/henrylee2cn/faygo/pongo2"
)
type (
// Tpl is template with modfied time.
Tpl struct {
template *pongo2.Template
modTime time.Time
}
// Render is a custom faygo template renderer using pongo2.
Render struct {
set *pongo2.TemplateSet
tplCache map[string]*Tpl
tplContext pongo2.Context // Context hold globle func for tpl
openCacheFile func(name string) (http.File, error)
caching bool // false=disable caching, true=enable caching
sync.RWMutex
}
)
// New creates a new Render instance with custom Options.
func newRender(openCacheFile func(name string) (http.File, error)) *Render {
return &Render{
set: pongo2.NewSet("faygo", pongo2.DefaultLoader),
tplCache: make(map[string]*Tpl),
tplContext: make(pongo2.Context),
openCacheFile: openCacheFile,
caching: openCacheFile != nil,
}
}
// TemplateVar sets the global template variable or function
func (render *Render) TemplateVar(name string, v interface{}) {
switch d := v.(type) {
case func(in *pongo2.Value, param *pongo2.Value) (out *pongo2.Value, err *pongo2.Error):
pongo2.RegisterFilter(name, d)
case pongo2.FilterFunction:
pongo2.RegisterFilter(name, d)
default:
render.tplContext[name] = d
}
}
// Render should render the template to the io.Writer.
func (render *Render) Render(filename string, data Map) ([]byte, error) {
if render.caching {
b, _, err := render.fromCache(filename, data, false)
return b, err
}
f, err := os.Open(filename)
if err != nil {
return nil, err
}
var fbytes []byte
fbytes, err = ioutil.ReadAll(f)
if err != nil {
return nil, err
}
fbytes, err = render.RenderFromBytesWithName(filename, fbytes, data)
return fbytes, err
}
// RenderFromBytesWithName should render the template to the io.Writer.
func (render *Render) RenderFromBytesWithName(filename string, fbytes []byte, data Map) ([]byte, error) {
template, err := render.set.FromBytesWithName(filename, fbytes)
if err != nil {
return nil, err
}
var data2 pongo2.Context
if data == nil {
data2 = render.tplContext
} else {
data2 = pongo2.Context(data)
for k, v := range render.tplContext {
if _, ok := data2[k]; !ok {
data2[k] = v
}
}
}
var b bytes.Buffer
err = template.ExecuteWriter(data2, &b)
return b.Bytes(), err
}
func (render *Render) fromCache(fname string, data Map, withInfo bool) ([]byte, os.FileInfo, error) {
// Get file content from the file system cache
f, err := render.openCacheFile(fname)
if err != nil {
render.Lock()
_, has := render.tplCache[fname]
if has {
delete(render.tplCache, fname)
}
render.Unlock()
return nil, nil, errors.New(fname + "is not find.")
}
defer f.Close()
fileInfo, err := f.Stat()
if err != nil {
return nil, nil, err
}
if fileInfo.IsDir() {
return nil, fileInfo, nil
}
render.RLock()
tplObj, has := render.tplCache[fname]
render.RUnlock()
var tpl *pongo2.Template
// When the template cache exists and the file is not updated
if has && tplObj.modTime.Equal(fileInfo.ModTime()) {
tpl = tplObj.template
} else {
// The cache template does not exist or the file is updated
render.Lock()
defer render.Unlock()
// Create a new template and cache it
fbytes, _ := ioutil.ReadAll(f)
tpl, err = render.set.FromBytesWithName(fname, fbytes)
if err != nil {
return nil, nil, err
}
render.tplCache[fname] = &Tpl{template: tpl, modTime: fileInfo.ModTime()}
}
var data2 pongo2.Context
if data == nil {
data2 = render.tplContext
} else {
data2 = pongo2.Context(data)
for k, v := range render.tplContext {
if _, ok := data2[k]; !ok {
data2[k] = v
}
}
}
var b bytes.Buffer
err = tpl.ExecuteWriter(data2, &b)
if withInfo {
return b.Bytes(), newNowFileInfo(fileInfo, int64(b.Len())), err
}
return b.Bytes(), nil, err
}
type nowFileInfo struct {
os.FileInfo
size int64
modTime time.Time
}
// Size returns the size in bytes for regular files; system-dependent for others
func (info *nowFileInfo) Size() int64 {
return info.size
}
// Mode returns file mode bits
func (info *nowFileInfo) ModTime() time.Time {
return info.modTime
}
func newNowFileInfo(info os.FileInfo, size int64) os.FileInfo {
return &nowFileInfo{
FileInfo: info,
size: size,
modTime: time.Now(),
}
}
func (render *Render) renderForFS(filename string, data Map) ([]byte, os.FileInfo, error) {
if render.caching {
return render.fromCache(filename, data, true)
}
f, err := os.Open(filename)
if err != nil {
return nil, nil, err
}
fileInfo, err := f.Stat()
if err != nil {
return nil, nil, err
}
if fileInfo.IsDir() {
return nil, fileInfo, nil
}
var fbytes []byte
fbytes, err = ioutil.ReadAll(f)
if err != nil {
return nil, nil, err
}
fbytes, err = render.RenderFromBytesWithName(filename, fbytes, data)
return fbytes, newNowFileInfo(fileInfo, int64(len(fbytes))), err
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/panrongcai/faygo.git
[email protected]:panrongcai/faygo.git
panrongcai
faygo
faygo
master

搜索帮助