add template test and cache module
Showing
4 changed files
with
204 additions
and
16 deletions
| ... | @@ -13,6 +13,8 @@ import ( | ... | @@ -13,6 +13,8 @@ import ( |
| 13 | "strconv" | 13 | "strconv" |
| 14 | ) | 14 | ) |
| 15 | 15 | ||
| 16 | const VERSION = "0.0.3" | ||
| 17 | |||
| 16 | var ( | 18 | var ( |
| 17 | BeeApp *App | 19 | BeeApp *App |
| 18 | AppName string | 20 | AppName string |
| ... | @@ -218,5 +220,6 @@ func Run() { | ... | @@ -218,5 +220,6 @@ func Run() { |
| 218 | GlobalSessions, _ = session.NewManager(SessionProvider, SessionName, SessionGCMaxLifetime) | 220 | GlobalSessions, _ = session.NewManager(SessionProvider, SessionName, SessionGCMaxLifetime) |
| 219 | go GlobalSessions.GC() | 221 | go GlobalSessions.GC() |
| 220 | } | 222 | } |
| 223 | BuildTemplate(ViewsPath) | ||
| 221 | BeeApp.Run() | 224 | BeeApp.Run() |
| 222 | } | 225 | } | ... | ... |
cache.go
0 → 100644
| 1 | package beego | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "errors" | ||
| 5 | "fmt" | ||
| 6 | "strconv" | ||
| 7 | "time" | ||
| 8 | ) | ||
| 9 | |||
| 10 | const ( | ||
| 11 | Kb = 1024 | ||
| 12 | Mb = 1024 * 1024 | ||
| 13 | Gb = 1024 * 1024 * 1024 | ||
| 14 | ) | ||
| 15 | |||
| 16 | var ( | ||
| 17 | DefaultEvery int = 60 // 1 minute | ||
| 18 | ) | ||
| 19 | |||
| 20 | var ( | ||
| 21 | InvalidCacheItem = errors.New("invalid cache item") | ||
| 22 | ItemIsDirectory = errors.New("can't cache a directory") | ||
| 23 | ItemNotInCache = errors.New("item not in cache") | ||
| 24 | ItemTooLarge = errors.New("item too large for cache") | ||
| 25 | WriteIncomplete = errors.New("incomplete write of cache item") | ||
| 26 | ) | ||
| 27 | |||
| 28 | type BeeItem struct { | ||
| 29 | val interface{} | ||
| 30 | Lastaccess time.Time | ||
| 31 | expired int | ||
| 32 | } | ||
| 33 | |||
| 34 | func (itm *BeeItem) Access() interface{} { | ||
| 35 | itm.Lastaccess = time.Now() | ||
| 36 | return itm.val | ||
| 37 | } | ||
| 38 | |||
| 39 | type BeeCache struct { | ||
| 40 | dur time.Duration | ||
| 41 | items map[string]*BeeItem | ||
| 42 | Every int // Run an expiration check Every seconds | ||
| 43 | } | ||
| 44 | |||
| 45 | // NewDefaultCache returns a new FileCache with sane defaults. | ||
| 46 | func NewBeeCache() *BeeCache { | ||
| 47 | cache := BeeCache{time.Since(time.Now()), | ||
| 48 | nil, | ||
| 49 | DefaultEvery} | ||
| 50 | return &cache | ||
| 51 | } | ||
| 52 | |||
| 53 | func (bc *BeeCache) Get(name string) interface{} { | ||
| 54 | itm, ok := bc.items[name] | ||
| 55 | if !ok { | ||
| 56 | return nil | ||
| 57 | } | ||
| 58 | return itm.Access() | ||
| 59 | } | ||
| 60 | |||
| 61 | func (bc *BeeCache) Put(name string, value interface{}, expired int) error { | ||
| 62 | t := BeeItem{val: value, Lastaccess: time.Now(), expired: expired} | ||
| 63 | if bc.IsExist(name) { | ||
| 64 | return errors.New("the key is exist") | ||
| 65 | } else { | ||
| 66 | bc.items[name] = &t | ||
| 67 | } | ||
| 68 | return nil | ||
| 69 | } | ||
| 70 | |||
| 71 | func (bc *BeeCache) Delete(name string) (ok bool, err error) { | ||
| 72 | _, ok = bc.items[name] | ||
| 73 | if !ok { | ||
| 74 | return | ||
| 75 | } | ||
| 76 | delete(bc.items, name) | ||
| 77 | _, valid := bc.items[name] | ||
| 78 | if valid { | ||
| 79 | ok = false | ||
| 80 | } | ||
| 81 | return | ||
| 82 | } | ||
| 83 | |||
| 84 | func (bc *BeeCache) IsExist(name string) bool { | ||
| 85 | _, ok := bc.items[name] | ||
| 86 | return ok | ||
| 87 | } | ||
| 88 | |||
| 89 | // Start activates the file cache; it will | ||
| 90 | func (bc *BeeCache) Start() error { | ||
| 91 | dur, err := time.ParseDuration(fmt.Sprintf("%ds", bc.Every)) | ||
| 92 | if err != nil { | ||
| 93 | return err | ||
| 94 | } | ||
| 95 | bc.dur = dur | ||
| 96 | bc.items = make(map[string]*BeeItem, 0) | ||
| 97 | go bc.vaccuum() | ||
| 98 | return nil | ||
| 99 | } | ||
| 100 | |||
| 101 | func (bc *BeeCache) vaccuum() { | ||
| 102 | if bc.Every < 1 { | ||
| 103 | return | ||
| 104 | } | ||
| 105 | for { | ||
| 106 | <-time.After(time.Duration(bc.dur)) | ||
| 107 | if bc.items == nil { | ||
| 108 | return | ||
| 109 | } | ||
| 110 | for name, _ := range bc.items { | ||
| 111 | if bc.item_expired(name) { | ||
| 112 | delete(bc.items, name) | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | // item_expired returns true if an item is expired. | ||
| 119 | func (bc *BeeCache) item_expired(name string) bool { | ||
| 120 | itm, ok := bc.items[name] | ||
| 121 | if !ok { | ||
| 122 | return true | ||
| 123 | } | ||
| 124 | dur := time.Now().Sub(itm.Lastaccess) | ||
| 125 | sec, err := strconv.Atoi(fmt.Sprintf("%0.0f", dur.Seconds())) | ||
| 126 | if err != nil { | ||
| 127 | return true | ||
| 128 | } else if sec >= itm.expired { | ||
| 129 | return true | ||
| 130 | } | ||
| 131 | return false | ||
| 132 | } |
| ... | @@ -15,7 +15,6 @@ import ( | ... | @@ -15,7 +15,6 @@ import ( |
| 15 | 15 | ||
| 16 | type Controller struct { | 16 | type Controller struct { |
| 17 | Ctx *Context | 17 | Ctx *Context |
| 18 | Tpl *template.Template | ||
| 19 | Data map[interface{}]interface{} | 18 | Data map[interface{}]interface{} |
| 20 | ChildName string | 19 | ChildName string |
| 21 | TplNames string | 20 | TplNames string |
| ... | @@ -39,8 +38,6 @@ type ControllerInterface interface { | ... | @@ -39,8 +38,6 @@ type ControllerInterface interface { |
| 39 | 38 | ||
| 40 | func (c *Controller) Init(ctx *Context, cn string) { | 39 | func (c *Controller) Init(ctx *Context, cn string) { |
| 41 | c.Data = make(map[interface{}]interface{}) | 40 | c.Data = make(map[interface{}]interface{}) |
| 42 | c.Tpl = template.New(cn + ctx.Request.Method) | ||
| 43 | c.Tpl = c.Tpl.Funcs(beegoTplFuncMap) | ||
| 44 | c.Layout = "" | 41 | c.Layout = "" |
| 45 | c.TplNames = "" | 42 | c.TplNames = "" |
| 46 | c.ChildName = cn | 43 | c.ChildName = cn |
| ... | @@ -91,17 +88,14 @@ func (c *Controller) Render() error { | ... | @@ -91,17 +88,14 @@ func (c *Controller) Render() error { |
| 91 | if c.TplNames == "" { | 88 | if c.TplNames == "" { |
| 92 | c.TplNames = c.ChildName + "/" + c.Ctx.Request.Method + "." + c.TplExt | 89 | c.TplNames = c.ChildName + "/" + c.Ctx.Request.Method + "." + c.TplExt |
| 93 | } | 90 | } |
| 94 | t, err := c.Tpl.ParseFiles(path.Join(ViewsPath, c.TplNames), path.Join(ViewsPath, c.Layout)) | ||
| 95 | if err != nil { | ||
| 96 | Trace("template ParseFiles err:", err) | ||
| 97 | } | ||
| 98 | _, file := path.Split(c.TplNames) | 91 | _, file := path.Split(c.TplNames) |
| 92 | subdir := path.Dir(c.TplNames) | ||
| 99 | newbytes := bytes.NewBufferString("") | 93 | newbytes := bytes.NewBufferString("") |
| 100 | t.ExecuteTemplate(newbytes, file, c.Data) | 94 | BeeTemplates[subdir].ExecuteTemplate(newbytes, file, c.Data) |
| 101 | tplcontent, _ := ioutil.ReadAll(newbytes) | 95 | tplcontent, _ := ioutil.ReadAll(newbytes) |
| 102 | c.Data["LayoutContent"] = template.HTML(string(tplcontent)) | 96 | c.Data["LayoutContent"] = template.HTML(string(tplcontent)) |
| 103 | _, file = path.Split(c.Layout) | 97 | _, file = path.Split(c.Layout) |
| 104 | err = t.ExecuteTemplate(c.Ctx.ResponseWriter, file, c.Data) | 98 | err := BeeTemplates[subdir].ExecuteTemplate(c.Ctx.ResponseWriter, file, c.Data) |
| 105 | if err != nil { | 99 | if err != nil { |
| 106 | Trace("template Execute err:", err) | 100 | Trace("template Execute err:", err) |
| 107 | } | 101 | } |
| ... | @@ -109,12 +103,9 @@ func (c *Controller) Render() error { | ... | @@ -109,12 +103,9 @@ func (c *Controller) Render() error { |
| 109 | if c.TplNames == "" { | 103 | if c.TplNames == "" { |
| 110 | c.TplNames = c.ChildName + "/" + c.Ctx.Request.Method + "." + c.TplExt | 104 | c.TplNames = c.ChildName + "/" + c.Ctx.Request.Method + "." + c.TplExt |
| 111 | } | 105 | } |
| 112 | t, err := c.Tpl.ParseFiles(path.Join(ViewsPath, c.TplNames)) | ||
| 113 | if err != nil { | ||
| 114 | Trace("template ParseFiles err:", err) | ||
| 115 | } | ||
| 116 | _, file := path.Split(c.TplNames) | 106 | _, file := path.Split(c.TplNames) |
| 117 | err = t.ExecuteTemplate(c.Ctx.ResponseWriter, file, c.Data) | 107 | subdir := path.Dir(c.TplNames) |
| 108 | err := BeeTemplates[subdir].ExecuteTemplate(c.Ctx.ResponseWriter, file, c.Data) | ||
| 118 | if err != nil { | 109 | if err != nil { |
| 119 | Trace("template Execute err:", err) | 110 | Trace("template Execute err:", err) |
| 120 | } | 111 | } | ... | ... |
| ... | @@ -3,18 +3,27 @@ package beego | ... | @@ -3,18 +3,27 @@ package beego |
| 3 | //@todo add template funcs | 3 | //@todo add template funcs |
| 4 | 4 | ||
| 5 | import ( | 5 | import ( |
| 6 | "fmt" | ||
| 7 | "errors" | 6 | "errors" |
| 7 | "fmt" | ||
| 8 | "github.com/russross/blackfriday" | 8 | "github.com/russross/blackfriday" |
| 9 | "html/template" | 9 | "html/template" |
| 10 | "os" | ||
| 11 | "path" | ||
| 12 | "path/filepath" | ||
| 10 | "strings" | 13 | "strings" |
| 11 | "time" | 14 | "time" |
| 12 | ) | 15 | ) |
| 13 | 16 | ||
| 14 | var beegoTplFuncMap template.FuncMap | 17 | var ( |
| 18 | beegoTplFuncMap template.FuncMap | ||
| 19 | BeeTemplates map[string]*template.Template | ||
| 20 | BeeTemplateExt string | ||
| 21 | ) | ||
| 15 | 22 | ||
| 16 | func init() { | 23 | func init() { |
| 24 | BeeTemplates = make(map[string]*template.Template) | ||
| 17 | beegoTplFuncMap = make(template.FuncMap) | 25 | beegoTplFuncMap = make(template.FuncMap) |
| 26 | BeeTemplateExt = "tpl" | ||
| 18 | beegoTplFuncMap["markdown"] = MarkDown | 27 | beegoTplFuncMap["markdown"] = MarkDown |
| 19 | beegoTplFuncMap["dateformat"] = DateFormat | 28 | beegoTplFuncMap["dateformat"] = DateFormat |
| 20 | beegoTplFuncMap["date"] = Date | 29 | beegoTplFuncMap["date"] = Date |
| ... | @@ -92,3 +101,56 @@ func AddFuncMap(key string, funname interface{}) error { | ... | @@ -92,3 +101,56 @@ func AddFuncMap(key string, funname interface{}) error { |
| 92 | beegoTplFuncMap[key] = funname | 101 | beegoTplFuncMap[key] = funname |
| 93 | return nil | 102 | return nil |
| 94 | } | 103 | } |
| 104 | |||
| 105 | type templatefile struct { | ||
| 106 | root string | ||
| 107 | files map[string][]string | ||
| 108 | } | ||
| 109 | |||
| 110 | func (self *templatefile) visit(paths string, f os.FileInfo, err error) error { | ||
| 111 | if f == nil { | ||
| 112 | return err | ||
| 113 | } | ||
| 114 | if f.IsDir() { | ||
| 115 | return nil | ||
| 116 | } else if (f.Mode() & os.ModeSymlink) > 0 { | ||
| 117 | return nil | ||
| 118 | } else { | ||
| 119 | if strings.HasSuffix(paths, BeeTemplateExt) { | ||
| 120 | a := []byte(paths) | ||
| 121 | a = a[len([]byte(self.root)):] | ||
| 122 | subdir := path.Dir(strings.TrimLeft(string(a), "/")) | ||
| 123 | if _, ok := self.files[subdir]; ok { | ||
| 124 | self.files[subdir] = append(self.files[subdir], paths) | ||
| 125 | } else { | ||
| 126 | m := make([]string, 1) | ||
| 127 | m[0] = paths | ||
| 128 | self.files[subdir] = m | ||
| 129 | } | ||
| 130 | |||
| 131 | } | ||
| 132 | } | ||
| 133 | return nil | ||
| 134 | } | ||
| 135 | |||
| 136 | func SetGlobalTemplateExt(ext string) { | ||
| 137 | BeeTemplateExt = ext | ||
| 138 | } | ||
| 139 | |||
| 140 | func BuildTemplate(dir string) error { | ||
| 141 | self := templatefile{ | ||
| 142 | root: dir, | ||
| 143 | files: make(map[string][]string), | ||
| 144 | } | ||
| 145 | err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { | ||
| 146 | return self.visit(path, f, err) | ||
| 147 | }) | ||
| 148 | if err != nil { | ||
| 149 | fmt.Printf("filepath.Walk() returned %v\n", err) | ||
| 150 | return err | ||
| 151 | } | ||
| 152 | for k, v := range self.files { | ||
| 153 | BeeTemplates[k] = template.Must(template.New("beegoTemplate" + k).Funcs(beegoTplFuncMap).ParseFiles(v...)) | ||
| 154 | } | ||
| 155 | return nil | ||
| 156 | } | ... | ... |
-
Please register or sign in to post a comment