From 3b890713604534cb505383f8101cd108dfb42180 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 14 Mar 2013 00:14:09 +0800 Subject: [PATCH] add template test and cache module --- beego.go | 3 ++ cache.go | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++ controller.go | 19 ++------ template.go | 68 ++++++++++++++++++++++++-- 4 files changed, 205 insertions(+), 17 deletions(-) create mode 100644 cache.go diff --git a/beego.go b/beego.go index 9a9a2758..b59cb850 100644 --- a/beego.go +++ b/beego.go @@ -13,6 +13,8 @@ import ( "strconv" ) +const VERSION = "0.0.3" + var ( BeeApp *App AppName string @@ -218,5 +220,6 @@ func Run() { GlobalSessions, _ = session.NewManager(SessionProvider, SessionName, SessionGCMaxLifetime) go GlobalSessions.GC() } + BuildTemplate(ViewsPath) BeeApp.Run() } diff --git a/cache.go b/cache.go new file mode 100644 index 00000000..287b26cc --- /dev/null +++ b/cache.go @@ -0,0 +1,132 @@ +package beego + +import ( + "errors" + "fmt" + "strconv" + "time" +) + +const ( + Kb = 1024 + Mb = 1024 * 1024 + Gb = 1024 * 1024 * 1024 +) + +var ( + DefaultEvery int = 60 // 1 minute +) + +var ( + InvalidCacheItem = errors.New("invalid cache item") + ItemIsDirectory = errors.New("can't cache a directory") + ItemNotInCache = errors.New("item not in cache") + ItemTooLarge = errors.New("item too large for cache") + WriteIncomplete = errors.New("incomplete write of cache item") +) + +type BeeItem struct { + val interface{} + Lastaccess time.Time + expired int +} + +func (itm *BeeItem) Access() interface{} { + itm.Lastaccess = time.Now() + return itm.val +} + +type BeeCache struct { + dur time.Duration + items map[string]*BeeItem + Every int // Run an expiration check Every seconds +} + +// NewDefaultCache returns a new FileCache with sane defaults. +func NewBeeCache() *BeeCache { + cache := BeeCache{time.Since(time.Now()), + nil, + DefaultEvery} + return &cache +} + +func (bc *BeeCache) Get(name string) interface{} { + itm, ok := bc.items[name] + if !ok { + return nil + } + return itm.Access() +} + +func (bc *BeeCache) Put(name string, value interface{}, expired int) error { + t := BeeItem{val: value, Lastaccess: time.Now(), expired: expired} + if bc.IsExist(name) { + return errors.New("the key is exist") + } else { + bc.items[name] = &t + } + return nil +} + +func (bc *BeeCache) Delete(name string) (ok bool, err error) { + _, ok = bc.items[name] + if !ok { + return + } + delete(bc.items, name) + _, valid := bc.items[name] + if valid { + ok = false + } + return +} + +func (bc *BeeCache) IsExist(name string) bool { + _, ok := bc.items[name] + return ok +} + +// Start activates the file cache; it will +func (bc *BeeCache) Start() error { + dur, err := time.ParseDuration(fmt.Sprintf("%ds", bc.Every)) + if err != nil { + return err + } + bc.dur = dur + bc.items = make(map[string]*BeeItem, 0) + go bc.vaccuum() + return nil +} + +func (bc *BeeCache) vaccuum() { + if bc.Every < 1 { + return + } + for { + <-time.After(time.Duration(bc.dur)) + if bc.items == nil { + return + } + for name, _ := range bc.items { + if bc.item_expired(name) { + delete(bc.items, name) + } + } + } +} + +// item_expired returns true if an item is expired. +func (bc *BeeCache) item_expired(name string) bool { + itm, ok := bc.items[name] + if !ok { + return true + } + dur := time.Now().Sub(itm.Lastaccess) + sec, err := strconv.Atoi(fmt.Sprintf("%0.0f", dur.Seconds())) + if err != nil { + return true + } else if sec >= itm.expired { + return true + } + return false +} diff --git a/controller.go b/controller.go index c51f155d..682aedf0 100644 --- a/controller.go +++ b/controller.go @@ -15,7 +15,6 @@ import ( type Controller struct { Ctx *Context - Tpl *template.Template Data map[interface{}]interface{} ChildName string TplNames string @@ -39,8 +38,6 @@ type ControllerInterface interface { func (c *Controller) Init(ctx *Context, cn string) { c.Data = make(map[interface{}]interface{}) - c.Tpl = template.New(cn + ctx.Request.Method) - c.Tpl = c.Tpl.Funcs(beegoTplFuncMap) c.Layout = "" c.TplNames = "" c.ChildName = cn @@ -91,17 +88,14 @@ func (c *Controller) Render() error { if c.TplNames == "" { c.TplNames = c.ChildName + "/" + c.Ctx.Request.Method + "." + c.TplExt } - t, err := c.Tpl.ParseFiles(path.Join(ViewsPath, c.TplNames), path.Join(ViewsPath, c.Layout)) - if err != nil { - Trace("template ParseFiles err:", err) - } _, file := path.Split(c.TplNames) + subdir := path.Dir(c.TplNames) newbytes := bytes.NewBufferString("") - t.ExecuteTemplate(newbytes, file, c.Data) + BeeTemplates[subdir].ExecuteTemplate(newbytes, file, c.Data) tplcontent, _ := ioutil.ReadAll(newbytes) c.Data["LayoutContent"] = template.HTML(string(tplcontent)) _, file = path.Split(c.Layout) - err = t.ExecuteTemplate(c.Ctx.ResponseWriter, file, c.Data) + err := BeeTemplates[subdir].ExecuteTemplate(c.Ctx.ResponseWriter, file, c.Data) if err != nil { Trace("template Execute err:", err) } @@ -109,12 +103,9 @@ func (c *Controller) Render() error { if c.TplNames == "" { c.TplNames = c.ChildName + "/" + c.Ctx.Request.Method + "." + c.TplExt } - t, err := c.Tpl.ParseFiles(path.Join(ViewsPath, c.TplNames)) - if err != nil { - Trace("template ParseFiles err:", err) - } _, file := path.Split(c.TplNames) - err = t.ExecuteTemplate(c.Ctx.ResponseWriter, file, c.Data) + subdir := path.Dir(c.TplNames) + err := BeeTemplates[subdir].ExecuteTemplate(c.Ctx.ResponseWriter, file, c.Data) if err != nil { Trace("template Execute err:", err) } diff --git a/template.go b/template.go index 32f973d7..888bb594 100644 --- a/template.go +++ b/template.go @@ -3,18 +3,27 @@ package beego //@todo add template funcs import ( - "fmt" "errors" + "fmt" "github.com/russross/blackfriday" "html/template" + "os" + "path" + "path/filepath" "strings" "time" ) -var beegoTplFuncMap template.FuncMap +var ( + beegoTplFuncMap template.FuncMap + BeeTemplates map[string]*template.Template + BeeTemplateExt string +) func init() { + BeeTemplates = make(map[string]*template.Template) beegoTplFuncMap = make(template.FuncMap) + BeeTemplateExt = "tpl" beegoTplFuncMap["markdown"] = MarkDown beegoTplFuncMap["dateformat"] = DateFormat beegoTplFuncMap["date"] = Date @@ -87,8 +96,61 @@ func Compare(a, b interface{}) (equal bool) { // AddFuncMap let user to register a func in the template func AddFuncMap(key string, funname interface{}) error { if _, ok := beegoTplFuncMap[key]; ok { - return errors.New("funcmap already has the key") + return errors.New("funcmap already has the key") } beegoTplFuncMap[key] = funname return nil } + +type templatefile struct { + root string + files map[string][]string +} + +func (self *templatefile) visit(paths string, f os.FileInfo, err error) error { + if f == nil { + return err + } + if f.IsDir() { + return nil + } else if (f.Mode() & os.ModeSymlink) > 0 { + return nil + } else { + if strings.HasSuffix(paths, BeeTemplateExt) { + a := []byte(paths) + a = a[len([]byte(self.root)):] + subdir := path.Dir(strings.TrimLeft(string(a), "/")) + if _, ok := self.files[subdir]; ok { + self.files[subdir] = append(self.files[subdir], paths) + } else { + m := make([]string, 1) + m[0] = paths + self.files[subdir] = m + } + + } + } + return nil +} + +func SetGlobalTemplateExt(ext string) { + BeeTemplateExt = ext +} + +func BuildTemplate(dir string) error { + self := templatefile{ + root: dir, + files: make(map[string][]string), + } + err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { + return self.visit(path, f, err) + }) + if err != nil { + fmt.Printf("filepath.Walk() returned %v\n", err) + return err + } + for k, v := range self.files { + BeeTemplates[k] = template.Must(template.New("beegoTemplate" + k).Funcs(beegoTplFuncMap).ParseFiles(v...)) + } + return nil +}