forked from foolin/goview
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathview.go
193 lines (168 loc) · 4.9 KB
/
view.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package goview
import (
"bytes"
"fmt"
"html/template"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
)
// HTMLContentType const templateEngineKey = "httpx_templateEngine"
var HTMLContentType = []string{"text/html; charset=utf-8"}
// DefaultConfig default config
var DefaultConfig = Config{
Root: "views",
Extension: ".html",
Master: "layouts/master",
Partials: []string{},
Funcs: make(template.FuncMap),
DisableCache: false,
Delims: Delims{Left: "{{", Right: "}}"},
}
// ViewEngine view template engine
type ViewEngine struct {
config Config
tplMap map[string]*template.Template
tplMutex sync.RWMutex
fileHandler FileHandler
}
// Config configuration options
type Config struct {
Root string //view root
Extension string //template extension
Master string //template master
Partials []string //template partial, such as head, foot
Funcs template.FuncMap //template functions
DisableCache bool //disable cache, debug mode
Delims Delims //delimeters
}
// M map interface for data
type M map[string]interface{}
// Delims delims for template
type Delims struct {
Left string
Right string
}
// FileHandler file handler interface
type FileHandler func(config Config, tplFile string) (content string, err error)
// New new template engine
func New(config Config) *ViewEngine {
return &ViewEngine{
config: config,
tplMap: make(map[string]*template.Template),
tplMutex: sync.RWMutex{},
fileHandler: DefaultFileHandler(),
}
}
// Default new default template engine
func Default() *ViewEngine {
return New(DefaultConfig)
}
// Render render template with http.ResponseWriter
func (e *ViewEngine) Render(w http.ResponseWriter, statusCode int, name string, data interface{}) error {
header := w.Header()
if val := header["Content-Type"]; len(val) == 0 {
header["Content-Type"] = HTMLContentType
}
w.WriteHeader(statusCode)
return e.executeRender(w, name, data)
}
// RenderWriter render template with io.Writer
func (e *ViewEngine) RenderWriter(w io.Writer, name string, data interface{}) error {
return e.executeRender(w, name, data)
}
func (e *ViewEngine) executeRender(out io.Writer, name string, data interface{}) error {
useMaster := true
if filepath.Ext(name) == e.config.Extension {
useMaster = false
name = strings.TrimSuffix(name, e.config.Extension)
}
return e.executeTemplate(out, name, data, useMaster)
}
func (e *ViewEngine) executeTemplate(out io.Writer, name string, data interface{}, useMaster bool) error {
var tpl *template.Template
var err error
var ok bool
allFuncs := make(template.FuncMap, 0)
allFuncs["include"] = func(layout string) (template.HTML, error) {
buf := new(bytes.Buffer)
err := e.executeTemplate(buf, layout, data, false)
return template.HTML(buf.String()), err
}
// Get the plugin collection
for k, v := range e.config.Funcs {
allFuncs[k] = v
}
e.tplMutex.RLock()
tpl, ok = e.tplMap[name]
e.tplMutex.RUnlock()
exeName := name
if useMaster && e.config.Master != "" {
exeName = e.config.Master
}
if !ok || e.config.DisableCache {
tplList := make([]string, 0)
if useMaster {
//render()
if e.config.Master != "" {
tplList = append(tplList, e.config.Master)
}
}
tplList = append(tplList, name)
tplList = append(tplList, e.config.Partials...)
// Loop through each template and test the full path
tpl = template.New(name).Funcs(allFuncs).Delims(e.config.Delims.Left, e.config.Delims.Right)
for _, v := range tplList {
var data string
data, err = e.fileHandler(e.config, v)
if err != nil {
return err
}
var tmpl *template.Template
if v == name {
tmpl = tpl
} else {
tmpl = tpl.New(v)
}
_, err = tmpl.Parse(data)
if err != nil {
return fmt.Errorf("ViewEngine render parser name:%v, error: %v", v, err)
}
}
e.tplMutex.Lock()
e.tplMap[name] = tpl
e.tplMutex.Unlock()
}
// Display the content to the screen
err = tpl.Funcs(allFuncs).ExecuteTemplate(out, exeName, data)
if err != nil {
return fmt.Errorf("ViewEngine execute template error: %v", err)
}
return nil
}
// SetFileHandler set file handler
func (e *ViewEngine) SetFileHandler(handle FileHandler) {
if handle == nil {
panic("FileHandler can't set nil!")
}
e.fileHandler = handle
}
// DefaultFileHandler new default file handler
func DefaultFileHandler() FileHandler {
return func(config Config, tplFile string) (content string, err error) {
// Get the absolute path of the root template
path, err := filepath.Abs(config.Root + string(os.PathSeparator) + tplFile + config.Extension)
if err != nil {
return "", fmt.Errorf("ViewEngine path:%v error: %v", path, err)
}
data, err := ioutil.ReadFile(path)
if err != nil {
return "", fmt.Errorf("ViewEngine render read name:%v, path:%v, error: %v", tplFile, path, err)
}
return string(data), nil
}
}