-
Notifications
You must be signed in to change notification settings - Fork 0
/
paser.go
263 lines (215 loc) · 6.93 KB
/
paser.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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
package templateManager
import (
"bytes"
"errors"
"fmt"
"html/template"
"io/ioutil"
"os"
"strings"
"github.com/dereking/grest/config"
"github.com/dereking/grest/log"
"go.uber.org/zap"
"github.com/fsnotify/fsnotify"
)
const (
LAYOUT_BODY_TAG = "{{ @RenderBody() }}"
LAYOUT_TEMPLATE_DIRNAME = "Shared" //layout模板的文件在views目录下的文件夹名称,区分大小写
LAYOUT_TEMPLATE_FILENAME = "_Layout" //layout模板的文件名路径,区分大小写,后缀有suffix确定
)
var (
LAYOUT_TEMPLATE_FILE string //layout模板的文件 路径,区分大小写.文件后缀必须小写。 views/Shared/_Layout.html
)
//var allTemplates map[string]*template.Template
var allTemplates *template.Template
var suffix string //模板文件后缀,包含. 大写
var TMPLATE_DIR string //模板存放目录,默认运行目录下的views , 不包含末尾斜杠
var LAYOUT_DATA string //layout文件的内容
var PthSep string //文件路径分隔符
var watcher *fsnotify.Watcher
//Initialize the templates.
// args
func Initialize() {
//TMPLATE_DIR : the directory of templates exists. eg: "views"
//suffix : the ext name of the template file. UPCASE. eg: ".HTML"
//bMoniteTemplate: bool, Need monite the template file modify, and auto reload it?
TMPLATE_DIR = config.AppConfig.StringDefault("TemplateDir", "views")
suffix = config.AppConfig.StringDefault("TemplateExt", ".HTML")
bMoniteTemplate := config.AppConfig.BoolDefault("AutoReloadTemplate", false)
PthSep = string(os.PathSeparator)
allTemplates = template.New("")
//注册模板函数
allTemplates = allTemplates.Funcs(template.FuncMap{"html": templateFunc_html})
allTemplates = allTemplates.Funcs(template.FuncMap{"fileSize": templateFunc_FileSize})
allTemplates = allTemplates.Funcs(template.FuncMap{"datetime": templateFunc_DateTime})
allTemplates = allTemplates.Funcs(template.FuncMap{"add": templateFunc_add})
LAYOUT_TEMPLATE_FILE := fmt.Sprintf("%s%s%s%s%s%s",
TMPLATE_DIR,
PthSep,
LAYOUT_TEMPLATE_DIRNAME,
PthSep,
LAYOUT_TEMPLATE_FILENAME,
strings.ToLower(suffix))
//读取layout数据
LAYOUT_DATA = load_layoutfile(LAYOUT_TEMPLATE_FILE)
if bMoniteTemplate {
var err error
watcher, err = fsnotify.NewWatcher()
if err != nil {
log.Logger().Error(" fsnotify.NewWatcher err", zap.Error(err))
}
loadTemplateDir("/", TMPLATE_DIR, true)
// Process events
go func() {
for {
select {
case ev := <-watcher.Events:
log.Logger().Debug("template modified, reload template:",
zap.String("templatename", ev.Name))
parseTemplate(ev.Name)
case err := <-watcher.Errors:
log.Logger().Error("error:", zap.Error(err))
}
}
}()
} else {
loadTemplateDir("/", TMPLATE_DIR, false)
}
for i, t := range allTemplates.Templates() {
log.Logger().Debug("allTemplates", zap.Any("index", i), zap.Any("t", t.Name()))
}
}
func load_layoutfile(fnRelative string) (ret string) {
//读取layout数据
content, err := ioutil.ReadFile(fnRelative)
if err != nil {
log.Logger().Error("Loading layout template err:", zap.Error(err),
zap.String("fnRelative", fnRelative))
} else {
ret = string(content)
}
return ret
}
//遍历模板目录,并进行模板编译。如果需要动态监视模板变动,进行目录监控。
// templateRoot 模板的当前目录。以/为根目录。
// dirRelative 相对目录
func loadTemplateDir(templateRoot, dirRelative string, bMoniteTemplate bool) {
fs, err := ioutil.ReadDir(dirRelative)
if err != nil {
log.Logger().Error("Loading template err:", zap.Error(err))
return
}
if bMoniteTemplate {
//moniter this dir
err = watcher.Add(dirRelative)
if err != nil {
log.Logger().Error("watcher.Watch err",
zap.String("TMPLATE_DIR", TMPLATE_DIR),
zap.Error(err))
}
}
for _, f := range fs {
//文件名。相对目录
fnRelative := dirRelative + PthSep + f.Name()
if f.IsDir() {
//递归子目录遍历
loadTemplateDir(templateRoot+strings.ToLower(f.Name())+"/", fnRelative, bMoniteTemplate)
} else {
//处理当前模板,编译并记录。
parseTemplate(fnRelative)
}
}
}
//编译指定模板。 fnRelative 为模板文件相对路径
func parseTemplate(fnRelative string) {
if !strings.HasSuffix(strings.ToUpper(fnRelative), strings.ToUpper(suffix)) {
return
}
//模板名字 /controller/action.html 全小写
tn := strings.Replace(fnRelative, TMPLATE_DIR, "", 1) //取相对路径
tn = strings.ToLower(strings.Replace(tn, "\\", "/", -1))
//如果是layout文件,直接退出函数,忽略掉。
// /shared/_layout.html
LAYOUT_TEMPLATE_Name := strings.ToLower(fmt.Sprintf("/%s/%s%s",
LAYOUT_TEMPLATE_DIRNAME,
LAYOUT_TEMPLATE_FILENAME,
suffix))
if strings.Compare(tn, LAYOUT_TEMPLATE_Name) == 0 {
//忽略 layout 文件
return
}
//检查模板是否存在,否则新建
t := allTemplates.Lookup(tn)
if t == nil {
log.Logger().Debug("New template",
zap.String("templateName", tn),
zap.String("templateFileName", fnRelative))
t = allTemplates.New(tn)
} else {
log.Logger().Warn("Overrided template",
zap.String("templateName", tn),
zap.String("templateFileName", fnRelative))
}
//编译模板
actionPageContent, err := ioutil.ReadFile(fnRelative)
if err != nil {
log.Logger().Error("parse",
zap.String("fnRelative", fnRelative),
zap.String("tn", tn),
zap.Error(err))
}
var strBody = string(actionPageContent)
// /views/shared目录之外的需要加载layout模板。
if strings.HasPrefix(tn, "/shared/") {
//log.Logger().Info("shared file, ", zap.String("templateName", tn))
} else {
// LAYOUT_DATA 不爲空那么 加载 layout 到当前模板
if len(LAYOUT_DATA) != 0 {
strBody = strings.Replace(LAYOUT_DATA,
LAYOUT_BODY_TAG,
string(actionPageContent), -1)
}
}
//编译
_, err = t.Parse(strBody)
if err != nil {
log.Logger().Error("parse",
zap.String("templateName", tn),
zap.String("templateFileName", fnRelative),
zap.Error(err))
}
}
func getTemplate(name string) *template.Template {
//return allTemplates[name]
return allTemplates.Lookup(name)
}
//Render execute the template.
// if template not found , renturn nil,error
func Render(ctlName, actName string, ViewData map[string]interface{}) ([]byte, error) {
//tname := fmt.Sprintf("views/%s/%s.html", ctlName, actName)
fn := strings.ToLower("/" + ctlName + "/" + actName + ".html")
t := getTemplate(fn)
if t == nil {
return nil, errors.New(fmt.Sprintf("template %s not found", fn))
/*
debug.Debug("Render HTML:", fn)
content, err := ioutil.ReadFile(fn)
if err != nil {
if os.IsNotExist(err) {
return nil, errors.New(fmt.Sprintf("template %s-%s not found", ctlName, actName))
}
}
debug.Debug("Render content:", string(content))
//t = allTemplates.New(tname)
t, _ = t.Parse(string(content))
// allTemplates[tname] = t
*/
}
b := bytes.NewBuffer(make([]byte, 0))
err := t.Execute(b, ViewData)
if err != nil {
return b.Bytes(), err
} else {
return b.Bytes(), nil
}
}