Greatly reduced the number of w.Write calls in the generated templates.
The topic list should only be rebuilt for a short window after something related to it changes on single server setups. Swapped out the RebuildGroupPermissions call in UpdatePerms with a more general and versable Groups.Reload call. The template generator now use log instead of fmt for writing debug logs.
This commit is contained in:
parent
f508ef9898
commit
1aac6f1268
|
@ -155,6 +155,7 @@ func (fps *MemoryForumPermsStore) Reload(fid int) error {
|
|||
}
|
||||
DebugDetailf("group.CanSee (length %d): %+v \n", len(group.CanSee), group.CanSee)
|
||||
}
|
||||
TopicListThaw.Thaw()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -121,6 +121,7 @@ func (mfs *MemoryForumStore) LoadForums() error {
|
|||
addForum(forum)
|
||||
}
|
||||
mfs.forumView.Store(forumView)
|
||||
TopicListThaw.Thaw()
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
|
@ -185,6 +186,7 @@ func (mfs *MemoryForumStore) BypassGet(id int) (*Forum, error) {
|
|||
forum.Link = BuildForumURL(NameToSlug(forum.Name), forum.ID)
|
||||
forum.LastTopic = Topics.DirtyGet(forum.LastTopicID)
|
||||
forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID)
|
||||
TopicListThaw.Thaw()
|
||||
|
||||
return forum, err
|
||||
}
|
||||
|
@ -213,6 +215,7 @@ func (mfs *MemoryForumStore) Reload(id int) error {
|
|||
forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID)
|
||||
|
||||
mfs.CacheSet(forum)
|
||||
TopicListThaw.Thaw()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -283,6 +286,7 @@ func (mfs *MemoryForumStore) Delete(id int) error {
|
|||
}
|
||||
_, err := mfs.delete.Exec(id)
|
||||
mfs.CacheDelete(id)
|
||||
TopicListThaw.Thaw()
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ func (group *Group) ChangeRank(isAdmin bool, isMod bool, isBanned bool) (err err
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Groups.Reload(group.ID)
|
||||
return nil
|
||||
}
|
||||
|
@ -68,7 +67,6 @@ func (group *Group) Update(name string, tag string) (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Groups.Reload(group.ID)
|
||||
return nil
|
||||
}
|
||||
|
@ -83,7 +81,7 @@ func (group *Group) UpdatePerms(perms map[string]bool) (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return RebuildGroupPermissions(group.ID)
|
||||
return Groups.Reload(group.ID)
|
||||
}
|
||||
|
||||
// Copy gives you a non-pointer concurrency safe copy of the group
|
||||
|
|
|
@ -90,6 +90,7 @@ func (mgs *MemoryGroupStore) LoadGroups() error {
|
|||
|
||||
DebugLog("Binding the Not Loggedin Group")
|
||||
GuestPerms = mgs.dirtyGetUnsafe(6).Perms
|
||||
TopicListThaw.Thaw()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -152,6 +153,7 @@ func (mgs *MemoryGroupStore) Reload(id int) error {
|
|||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
TopicListThaw.Thaw()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -280,6 +282,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
|||
mgs.groupCount++
|
||||
mgs.Unlock()
|
||||
|
||||
TopicListThaw.Thaw()
|
||||
return gid, FPStore.ReloadAll()
|
||||
//return gid, TopicList.RebuildPermTree()
|
||||
}
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
package tmpl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Fragment struct {
|
||||
Body string
|
||||
TemplateName string
|
||||
Index int
|
||||
Seen bool
|
||||
}
|
||||
|
||||
type OutBufferFrame struct {
|
||||
Body string
|
||||
Type string
|
||||
TemplateName string
|
||||
Extra interface{}
|
||||
Extra2 interface{}
|
||||
}
|
||||
|
||||
type CContext struct {
|
||||
|
@ -18,39 +26,12 @@ type CContext struct {
|
|||
OutBuf *[]OutBufferFrame
|
||||
}
|
||||
|
||||
func (con *CContext) Push(nType string, body string) {
|
||||
*con.OutBuf = append(*con.OutBuf, OutBufferFrame{body, nType, con.TemplateName})
|
||||
func (con *CContext) Push(nType string, body string) (index int) {
|
||||
*con.OutBuf = append(*con.OutBuf, OutBufferFrame{body, nType, con.TemplateName, nil, nil})
|
||||
return len(*con.OutBuf) - 1
|
||||
}
|
||||
|
||||
func (con *CContext) GetLastType() string {
|
||||
outBuf := *con.OutBuf
|
||||
if len(outBuf) == 0 {
|
||||
return ""
|
||||
}
|
||||
return outBuf[len(outBuf)-1].Type
|
||||
}
|
||||
|
||||
func (con *CContext) GetLastBody() string {
|
||||
outBuf := *con.OutBuf
|
||||
if len(outBuf) == 0 {
|
||||
return ""
|
||||
}
|
||||
return outBuf[len(outBuf)-1].Body
|
||||
}
|
||||
|
||||
func (con *CContext) SetLastBody(newBody string) error {
|
||||
outBuf := *con.OutBuf
|
||||
if len(outBuf) == 0 {
|
||||
return errors.New("outbuf is empty")
|
||||
}
|
||||
outBuf[len(outBuf)-1].Body = newBody
|
||||
return nil
|
||||
}
|
||||
|
||||
func (con *CContext) GetLastTemplate() string {
|
||||
outBuf := *con.OutBuf
|
||||
if len(outBuf) == 0 {
|
||||
return ""
|
||||
}
|
||||
return outBuf[len(outBuf)-1].TemplateName
|
||||
func (con *CContext) PushText(body string, fragIndex int, fragOutIndex int) (index int) {
|
||||
*con.OutBuf = append(*con.OutBuf, OutBufferFrame{body, "text", con.TemplateName, fragIndex, fragOutIndex})
|
||||
return len(*con.OutBuf) - 1
|
||||
}
|
||||
|
|
|
@ -40,12 +40,6 @@ type CTemplateConfig struct {
|
|||
PackageName string
|
||||
}
|
||||
|
||||
type Fragment struct {
|
||||
Body string
|
||||
TemplateName string
|
||||
Index int
|
||||
}
|
||||
|
||||
// nolint
|
||||
type CTemplateSet struct {
|
||||
templateList map[string]*parse.Tree
|
||||
|
@ -53,7 +47,7 @@ type CTemplateSet struct {
|
|||
funcMap map[string]interface{}
|
||||
importMap map[string]string
|
||||
TemplateFragmentCount map[string]int
|
||||
Fragments map[string]int
|
||||
FragOnce map[string]bool
|
||||
fragmentCursor map[string]int
|
||||
FragOut string
|
||||
fragBuf []Fragment
|
||||
|
@ -122,6 +116,16 @@ func (c *CTemplateSet) SetBuildTags(tags string) {
|
|||
c.buildTags = tags
|
||||
}
|
||||
|
||||
type SkipBlock struct {
|
||||
Frags map[int]int
|
||||
LastCount int
|
||||
ClosestFragSkip int
|
||||
}
|
||||
type Skipper struct {
|
||||
Count int
|
||||
Index int
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (out string, err error) {
|
||||
if c.config.Debug {
|
||||
fmt.Println("Compiling template '" + name + "'")
|
||||
|
@ -173,10 +177,11 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
|
|||
c.localVars = make(map[string]map[string]VarItemReflect)
|
||||
c.localVars[fname] = make(map[string]VarItemReflect)
|
||||
c.localVars[fname]["."] = VarItemReflect{".", con.VarHolder, con.HoldReflect}
|
||||
if c.Fragments == nil {
|
||||
c.Fragments = make(map[string]int)
|
||||
if c.FragOnce == nil {
|
||||
c.FragOnce = make(map[string]bool)
|
||||
}
|
||||
c.fragmentCursor = map[string]int{fname: 0}
|
||||
c.fragBuf = nil
|
||||
c.langIndexToName = nil
|
||||
|
||||
// TODO: Is this the first template loaded in? We really should have some sort of constructor for CTemplateSet
|
||||
|
@ -187,6 +192,11 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
|
|||
c.rootIterate(c.templateList[fname], con)
|
||||
c.TemplateFragmentCount[fname] = c.fragmentCursor[fname] + 1
|
||||
|
||||
_, ok := c.FragOnce[fname]
|
||||
if !ok {
|
||||
c.FragOnce[fname] = true
|
||||
}
|
||||
|
||||
if len(c.langIndexToName) > 0 {
|
||||
c.importMap[langPkg] = langPkg
|
||||
}
|
||||
|
@ -195,7 +205,6 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
|
|||
for _, item := range c.importMap {
|
||||
importList += "import \"" + item + "\"\n"
|
||||
}
|
||||
|
||||
var varString string
|
||||
for _, varItem := range c.varList {
|
||||
varString += "var " + varItem.Name + " " + varItem.Type + " = " + varItem.Destination + "\n"
|
||||
|
@ -205,7 +214,6 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
|
|||
if c.buildTags != "" {
|
||||
fout += "// +build " + c.buildTags + "\n\n"
|
||||
}
|
||||
|
||||
fout += "// Code generated by Gosora. More below:\n/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */\n"
|
||||
fout += "package " + c.config.PackageName + "\n" + importList + "\n"
|
||||
|
||||
|
@ -238,20 +246,74 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
|
|||
fout += "var plist = phrases.GetTmplPhrasesBytes(" + fname + "_tmpl_phrase_id)\n"
|
||||
}
|
||||
fout += varString
|
||||
for _, frame := range outBuf {
|
||||
|
||||
var skipped = make(map[string]*SkipBlock) // map[templateName]*SkipBlock{map[atIndexAndAfter]skipThisMuch,lastCount}
|
||||
|
||||
var writeTextFrame = func(tmplName string, index int) {
|
||||
out := "w.Write(" + tmplName + "_frags[" + strconv.Itoa(index) + "]" + ")\n"
|
||||
c.detail("writing ", out)
|
||||
fout += out
|
||||
}
|
||||
|
||||
for fid := 0; len(outBuf) > fid; fid++ {
|
||||
frame := outBuf[fid]
|
||||
if frame.Type == "text" {
|
||||
c.detail("text frame:")
|
||||
c.detail(frame)
|
||||
oid := fid
|
||||
skipBlock, ok := skipped[frame.TemplateName]
|
||||
if !ok {
|
||||
skipBlock = &SkipBlock{make(map[int]int), 0, 0}
|
||||
skipped[frame.TemplateName] = skipBlock
|
||||
}
|
||||
skip := skipBlock.LastCount
|
||||
c.detailf("skipblock %+v\n", skipBlock)
|
||||
for len(outBuf) > fid+1 && outBuf[fid+1].Type == "text" && outBuf[fid+1].TemplateName == frame.TemplateName {
|
||||
next := outBuf[fid+1]
|
||||
c.detail("next frame:", next)
|
||||
c.detail("frame frag:", c.fragBuf[frame.Extra2.(int)])
|
||||
c.detail("next frag:", c.fragBuf[next.Extra2.(int)])
|
||||
c.fragBuf[frame.Extra2.(int)].Body += c.fragBuf[next.Extra2.(int)].Body
|
||||
c.fragBuf[next.Extra2.(int)].Seen = true
|
||||
fid++
|
||||
skipBlock.LastCount += (fid - oid)
|
||||
skipBlock.Frags[frame.Extra.(int)] = skipBlock.LastCount
|
||||
}
|
||||
writeTextFrame(frame.TemplateName, frame.Extra.(int)-skip)
|
||||
} else {
|
||||
c.detail(frame.Type + " frame")
|
||||
fout += frame.Body
|
||||
}
|
||||
}
|
||||
fout += "return nil\n}\n"
|
||||
|
||||
var writeFrag = func(tmplName string, index int, body string) {
|
||||
fragmentPrefix := tmplName + "_frags[" + strconv.Itoa(index) + "]" + " = []byte(`" + body + "`)\n"
|
||||
c.detail("writing ", fragmentPrefix)
|
||||
c.FragOut += fragmentPrefix
|
||||
}
|
||||
|
||||
for _, frag := range c.fragBuf {
|
||||
c.detail("frag: ", frag)
|
||||
if frag.Seen {
|
||||
c.detail("invisible")
|
||||
continue
|
||||
}
|
||||
skipBlock := skipped[frag.TemplateName]
|
||||
skip := skipBlock.Frags[skipBlock.ClosestFragSkip]
|
||||
_, ok := skipBlock.Frags[frag.Index]
|
||||
if ok {
|
||||
skipBlock.ClosestFragSkip = frag.Index
|
||||
}
|
||||
c.detailf("skipblock %+v\n", skipBlock)
|
||||
c.detail("skipping ", skip)
|
||||
writeFrag(frag.TemplateName, frag.Index-skip, frag.Body)
|
||||
}
|
||||
|
||||
fout = strings.Replace(fout, `))
|
||||
w.Write([]byte(`, " + ", -1)
|
||||
fout = strings.Replace(fout, "` + `", "", -1)
|
||||
|
||||
for _, frag := range c.fragBuf {
|
||||
fragmentPrefix := frag.TemplateName + "_frags[" + strconv.Itoa(frag.Index) + "]"
|
||||
c.FragOut += fragmentPrefix + " = []byte(`" + frag.Body + "`)\n"
|
||||
}
|
||||
|
||||
if c.config.Debug {
|
||||
for index, count := range c.stats {
|
||||
fmt.Println(index+": ", strconv.Itoa(count))
|
||||
|
@ -264,6 +326,7 @@ w.Write([]byte(`, " + ", -1)
|
|||
}
|
||||
|
||||
func (c *CTemplateSet) rootIterate(tree *parse.Tree, con CContext) {
|
||||
c.dumpCall("rootIterate", tree, con)
|
||||
c.detail(tree.Root)
|
||||
treeLength := len(tree.Root.Nodes)
|
||||
for index, node := range tree.Root.Nodes {
|
||||
|
@ -275,10 +338,12 @@ func (c *CTemplateSet) rootIterate(tree *parse.Tree, con CContext) {
|
|||
}
|
||||
c.compileSwitch(con, node)
|
||||
}
|
||||
c.retCall("rootIterate")
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) compileSwitch(con CContext, node parse.Node) {
|
||||
c.dumpCall("compileSwitch", con, node)
|
||||
defer c.retCall("compileSwitch")
|
||||
switch node := node.(type) {
|
||||
case *parse.ActionNode:
|
||||
c.detail("Action Node")
|
||||
|
@ -333,15 +398,12 @@ func (c *CTemplateSet) compileSwitch(con CContext, node parse.Node) {
|
|||
return
|
||||
}
|
||||
|
||||
fragmentName := con.TemplateName + "_" + strconv.Itoa(c.fragmentCursor[con.TemplateName])
|
||||
fragmentPrefix := con.TemplateName + "_frags[" + strconv.Itoa(c.fragmentCursor[con.TemplateName]) + "]"
|
||||
_, ok := c.Fragments[fragmentName]
|
||||
if !ok {
|
||||
c.Fragments[fragmentName] = len(node.Text)
|
||||
c.fragBuf = append(c.fragBuf, Fragment{string(node.Text), con.TemplateName, c.fragmentCursor[con.TemplateName]})
|
||||
}
|
||||
c.fragmentCursor[con.TemplateName] = c.fragmentCursor[con.TemplateName] + 1
|
||||
con.Push("text", "w.Write("+fragmentPrefix+")\n")
|
||||
nodeText := string(node.Text)
|
||||
fragIndex := c.fragmentCursor[con.TemplateName]
|
||||
_, ok := c.FragOnce[con.TemplateName]
|
||||
c.fragBuf = append(c.fragBuf, Fragment{nodeText, con.TemplateName, fragIndex, ok})
|
||||
con.PushText(strconv.Itoa(fragIndex), fragIndex, len(c.fragBuf)-1)
|
||||
c.fragmentCursor[con.TemplateName] = fragIndex + 1
|
||||
default:
|
||||
c.unknownNode(node)
|
||||
}
|
||||
|
@ -349,6 +411,7 @@ func (c *CTemplateSet) compileSwitch(con CContext, node parse.Node) {
|
|||
|
||||
func (c *CTemplateSet) compileRangeNode(con CContext, node *parse.RangeNode) {
|
||||
c.dumpCall("compileRangeNode", con, node)
|
||||
defer c.retCall("compileRangeNode")
|
||||
c.detail("node.Pipe: ", node.Pipe)
|
||||
var expr string
|
||||
var outVal reflect.Value
|
||||
|
@ -653,8 +716,11 @@ func (c *CTemplateSet) compareJoin(con CContext, pos int, node *parse.CommandNod
|
|||
|
||||
func (c *CTemplateSet) compileIdentSwitch(con CContext, node *parse.CommandNode) (out string, val reflect.Value, literal bool) {
|
||||
c.dumpCall("compileIdentSwitch", con, node)
|
||||
var litString = func(inner string) {
|
||||
out = "w.Write([]byte(" + inner + "))\n"
|
||||
var litString = func(inner string, bytes bool) {
|
||||
if !bytes {
|
||||
inner = "[]byte(" + inner + ")"
|
||||
}
|
||||
out = "w.Write(" + inner + ")\n"
|
||||
literal = true
|
||||
}
|
||||
ArgLoop:
|
||||
|
@ -682,7 +748,7 @@ ArgLoop:
|
|||
leftParam, _ := c.compileIfVarSub(con, leftOperand)
|
||||
// TODO: Refactor this
|
||||
// TODO: Validate that this is actually a time.Time
|
||||
litString("time.Since(" + leftParam + ").String()")
|
||||
litString("time.Since("+leftParam+").String()", false)
|
||||
c.importMap["time"] = "time"
|
||||
break ArgLoop
|
||||
case "dock":
|
||||
|
@ -692,7 +758,6 @@ ArgLoop:
|
|||
if len(leftOperand) == 0 || len(rightOperand) == 0 {
|
||||
panic("The left or right operand for function dock cannot be left blank")
|
||||
}
|
||||
|
||||
leftParam := leftOperand
|
||||
if leftOperand[0] != '"' {
|
||||
leftParam, _ = c.compileIfVarSub(con, leftParam)
|
||||
|
@ -707,7 +772,7 @@ ArgLoop:
|
|||
val = val3
|
||||
|
||||
// TODO: Refactor this
|
||||
litString("common.BuildWidget(" + leftParam + "," + rightParam + ")")
|
||||
litString("common.BuildWidget("+leftParam+","+rightParam+")", false)
|
||||
break ArgLoop
|
||||
case "lang":
|
||||
// TODO: Implement string literals properly
|
||||
|
@ -718,11 +783,10 @@ ArgLoop:
|
|||
if leftOperand[0] != '"' {
|
||||
panic("Phrase names cannot be dynamic")
|
||||
}
|
||||
|
||||
// ! Slightly crude but it does the job
|
||||
leftParam := strings.Replace(leftOperand, "\"", "", -1)
|
||||
c.langIndexToName = append(c.langIndexToName, leftParam)
|
||||
litString("plist[" + strconv.Itoa(len(c.langIndexToName)-1) + "]")
|
||||
litString("plist["+strconv.Itoa(len(c.langIndexToName)-1)+"]", true)
|
||||
break ArgLoop
|
||||
case "level":
|
||||
// TODO: Implement level literals
|
||||
|
@ -732,7 +796,7 @@ ArgLoop:
|
|||
}
|
||||
leftParam, _ := c.compileIfVarSub(con, leftOperand)
|
||||
// TODO: Refactor this
|
||||
litString("phrases.GetLevelPhrase(" + leftParam + ")")
|
||||
litString("phrases.GetLevelPhrase("+leftParam+")", false)
|
||||
c.importMap[langPkg] = langPkg
|
||||
break ArgLoop
|
||||
case "scope":
|
||||
|
@ -991,6 +1055,7 @@ func (c *CTemplateSet) retCall(name string, params ...interface{}) {
|
|||
|
||||
func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.Value, assLines string, onEnd func(string) string) {
|
||||
c.dumpCall("compileVarSub", con, varname, val, assLines, onEnd)
|
||||
defer c.retCall("compileVarSub")
|
||||
if onEnd == nil {
|
||||
onEnd = func(in string) string {
|
||||
return in
|
||||
|
@ -999,7 +1064,7 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V
|
|||
|
||||
// Is this a literal string?
|
||||
if len(varname) != 0 && varname[0] == '"' {
|
||||
con.Push("varsub", onEnd(assLines+"w.Write([]byte("+varname+"))\n"))
|
||||
con.Push("lvarsub", onEnd(assLines+"w.Write([]byte("+varname+"))\n"))
|
||||
return
|
||||
}
|
||||
for _, varItem := range c.varList {
|
||||
|
@ -1030,17 +1095,23 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V
|
|||
switch val.Kind() {
|
||||
case reflect.Int:
|
||||
c.importMap["strconv"] = "strconv"
|
||||
base = "w.Write([]byte(strconv.Itoa(" + varname + ")))\n"
|
||||
base = "[]byte(strconv.Itoa(" + varname + "))"
|
||||
case reflect.Bool:
|
||||
base = "if " + varname + " {\nw.Write([]byte(\"true\"))} else {\nw.Write([]byte(\"false\"))\n}\n"
|
||||
con.Push("startif", "if "+varname+" {\n")
|
||||
con.Push("varsub", "w.Write([]byte(\"true\"))")
|
||||
con.Push("endif", "} ")
|
||||
con.Push("startelse", "else {\n")
|
||||
con.Push("varsub", "w.Write([]byte(\"false\"))")
|
||||
con.Push("endelse", "}\n")
|
||||
return
|
||||
case reflect.String:
|
||||
if val.Type().Name() != "string" && !strings.HasPrefix(varname, "string(") {
|
||||
varname = "string(" + varname + ")"
|
||||
}
|
||||
base = "w.Write([]byte(" + varname + "))\n"
|
||||
base = "[]byte(" + varname + ")"
|
||||
case reflect.Int64:
|
||||
c.importMap["strconv"] = "strconv"
|
||||
base = "w.Write([]byte(strconv.FormatInt(" + varname + ", 10)))\n"
|
||||
base = "[]byte(strconv.FormatInt(" + varname + ", 10))"
|
||||
default:
|
||||
if !val.IsValid() {
|
||||
panic(assLines + varname + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||
|
@ -1050,12 +1121,17 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V
|
|||
fmt.Println("Unknown Type:", val.Type().Name())
|
||||
panic("-- I don't know what this variable's type is o.o\n")
|
||||
}
|
||||
base = "w.Write(" + base + ")\n"
|
||||
c.detail("base: ", base)
|
||||
con.Push("varsub", onEnd(assLines+base))
|
||||
if assLines == "" {
|
||||
con.Push("varsub", base)
|
||||
} else {
|
||||
con.Push("lvarsub", onEnd(assLines+base))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNode) {
|
||||
c.debugCall("compileSubTemplate", pcon, node)
|
||||
c.dumpCall("compileSubTemplate", pcon, node)
|
||||
c.detail("Template Node: ", node.Name)
|
||||
|
||||
fname := strings.TrimSuffix(node.Name, filepath.Ext(node.Name))
|
||||
|
@ -1107,26 +1183,33 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
|
|||
c.localVars[fname] = make(map[string]VarItemReflect)
|
||||
c.localVars[fname]["."] = VarItemReflect{".", con.VarHolder, con.HoldReflect}
|
||||
c.fragmentCursor[fname] = 0
|
||||
con.Push("starttemplate", "{\n")
|
||||
c.rootIterate(subtree, con)
|
||||
con.Push("endtemplate", "}\n")
|
||||
c.TemplateFragmentCount[fname] = c.fragmentCursor[fname] + 1
|
||||
|
||||
_, ok := c.FragOnce[fname]
|
||||
if !ok {
|
||||
c.FragOnce[fname] = true
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should we rethink the way the log methods work or their names?
|
||||
|
||||
func (c *CTemplateSet) detail(args ...interface{}) {
|
||||
if c.config.SuperDebug {
|
||||
fmt.Println(args...)
|
||||
log.Println(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) detailf(left string, args ...interface{}) {
|
||||
if c.config.SuperDebug {
|
||||
fmt.Printf(left, args...)
|
||||
log.Printf(left, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) error(args ...interface{}) {
|
||||
if c.config.Debug {
|
||||
fmt.Println(args...)
|
||||
log.Println(args...)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var TopicListThaw ThawInt
|
||||
|
||||
type ThawInt interface {
|
||||
Thawed() bool
|
||||
Thaw()
|
||||
|
||||
Tick() error
|
||||
}
|
||||
|
||||
type SingleServerThaw struct {
|
||||
DefaultThaw
|
||||
}
|
||||
|
||||
func NewSingleServerThaw() *SingleServerThaw {
|
||||
thaw := &SingleServerThaw{}
|
||||
if Config.ServerCount == 1 {
|
||||
AddScheduledSecondTask(thaw.Tick)
|
||||
}
|
||||
return thaw
|
||||
}
|
||||
|
||||
func (thaw *SingleServerThaw) Thawed() bool {
|
||||
if Config.ServerCount == 1 {
|
||||
return thaw.DefaultThaw.Thawed()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (thaw *SingleServerThaw) Thaw() {
|
||||
if Config.ServerCount == 1 {
|
||||
thaw.DefaultThaw.Thaw()
|
||||
}
|
||||
}
|
||||
|
||||
type DefaultThaw struct {
|
||||
thawed int64
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewDefaultThaw() *DefaultThaw {
|
||||
thaw := &DefaultThaw{}
|
||||
AddScheduledSecondTask(thaw.Tick)
|
||||
return thaw
|
||||
}
|
||||
|
||||
// Decrement the thawed counter once a second until it goes cold
|
||||
func (thaw *DefaultThaw) Tick() error {
|
||||
thaw.Lock()
|
||||
defer thaw.Unlock()
|
||||
prior := thaw.thawed
|
||||
if prior > 0 {
|
||||
atomic.StoreInt64(&thaw.thawed, prior-1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (thaw *DefaultThaw) Thawed() bool {
|
||||
return thaw.thawed > 0
|
||||
}
|
||||
|
||||
func (thaw *DefaultThaw) Thaw() {
|
||||
atomic.StoreInt64(&thaw.thawed, 5)
|
||||
}
|
|
@ -192,6 +192,7 @@ func (topic *Topic) cacheRemove() {
|
|||
if tcache != nil {
|
||||
tcache.Remove(topic.ID)
|
||||
}
|
||||
TopicListThaw.Thaw()
|
||||
}
|
||||
|
||||
// TODO: Write a test for this
|
||||
|
@ -259,6 +260,7 @@ func (topic *Topic) Like(score int, uid int) (err error) {
|
|||
|
||||
// TODO: Implement this
|
||||
func (topic *Topic) Unlike(uid int) error {
|
||||
topic.cacheRemove()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,12 @@ func NewDefaultTopicList() (*DefaultTopicList, error) {
|
|||
}
|
||||
|
||||
func (tList *DefaultTopicList) Tick() error {
|
||||
//fmt.Println("TopicList.Tick")
|
||||
if !TopicListThaw.Thawed() {
|
||||
return nil
|
||||
}
|
||||
//fmt.Println("building topic list")
|
||||
|
||||
var oddLists = make(map[int]*TopicListHolder)
|
||||
var evenLists = make(map[int]*TopicListHolder)
|
||||
|
||||
|
|
|
@ -113,6 +113,7 @@ func (mts *DefaultTopicStore) Reload(id int) error {
|
|||
} else {
|
||||
_ = mts.cache.Remove(id)
|
||||
}
|
||||
TopicListThaw.Thaw()
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -181,6 +181,7 @@ func (user *User) CacheRemove() {
|
|||
if ucache != nil {
|
||||
ucache.Remove(user.ID)
|
||||
}
|
||||
TopicListThaw.Thaw()
|
||||
}
|
||||
|
||||
func (user *User) Ban(duration time.Duration, issuedBy int) error {
|
||||
|
|
|
@ -60,7 +60,7 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
|
|||
exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""),
|
||||
register: acc.Insert("users").Columns("name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt, lastLiked, oldestItemLikedCreatedAt").Fields("?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), // TODO: Implement user_count on users_groups here
|
||||
usernameExists: acc.SimpleSelect("users", "name", "name = ?", "", ""),
|
||||
userCount: acc.SimpleCount("users", "", ""),
|
||||
userCount: acc.Count("users").Prepare(),
|
||||
}, acc.FirstError()
|
||||
}
|
||||
|
||||
|
@ -244,6 +244,7 @@ func (mus *DefaultUserStore) Reload(id int) error {
|
|||
|
||||
user.Init()
|
||||
_ = mus.cache.Set(user)
|
||||
TopicListThaw.Thaw()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -270,12 +271,10 @@ func (mus *DefaultUserStore) Create(username string, password string, email stri
|
|||
if err != ErrNoRows {
|
||||
return 0, ErrAccountExists
|
||||
}
|
||||
|
||||
salt, err := GenerateSafeString(SaltLength)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password+salt), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
|
|
@ -69,6 +69,7 @@ func gloinit() (err error) {
|
|||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
common.TopicListThaw = common.NewSingleServerThaw()
|
||||
common.SwitchToTestDB()
|
||||
|
||||
var ok bool
|
||||
|
|
Loading…
Reference in New Issue