Last active
October 4, 2016 11:08
-
-
Save mh-cbon/9eb7701e57a8f37bb7e62647dc9726e8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
func main () { | |
get := watcher.HotTemplateReloader("a.tmpl", "b.tmpl") | |
// following should be thread safe, and do the hot reload of t template instance | |
t, err := get() | |
if err!=nil { | |
panic(err) | |
} | |
if err := t.(*template.Template).Execute(some io.Writer, struct{}{}); err != nil { | |
panic(err) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package watcher | |
import ( | |
"html/template" | |
"path/filepath" | |
) | |
func HotTemplateReloader(templates ...string) func () (*template.Template, error) { | |
checker := NewFsChecker(templates...) | |
_, need := WatchThis (ProduceTemplates, checker.Check, DelivererBlindly, templates...) | |
return func () (*template.Template, error) { | |
res := make(chan ProducedResult) | |
need<-res | |
p:=<-res | |
return p.Result.(*template.Template), p.Err | |
} | |
} | |
func ProduceTemplates(templates ...string) (interface{}, error) { | |
return template. | |
New(filepath.Base(templates[0])). | |
// Funcs(gtf.GtfFuncMap). | |
ParseFiles(templates...) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package watcher | |
import ( | |
"time" | |
"os" | |
"path/filepath" | |
) | |
type ProducerFunc func (data ...string) (result interface{}, err error) | |
type CheckerFunc func () (changed bool, err error) | |
type DeliverStrategy func (res chan ProducedResult, p ProducedResult) | |
type ProducedResult struct { | |
Result interface{} | |
Err error | |
} | |
type ProduceQuery struct { | |
Some []string | |
Res chan *ProducedResult | |
} | |
func DelivererBlindly(res chan ProducedResult, p ProducedResult) { | |
res<-p | |
} | |
type DeliverOnlyValidResults struct { | |
result ProducedResult | |
} | |
func (d *DeliverOnlyValidResults) Deliver(res chan ProducedResult, p ProducedResult) { | |
if p.Err==nil && p.Result!=nil { | |
d.result = p | |
} | |
res<-d.result | |
} | |
type FsChecker struct { | |
inputPaths []string | |
lastMod *time.Time | |
} | |
func NewFsChecker(some ...string) FsChecker { | |
ret := FsChecker{ | |
inputPaths: some, | |
} | |
return ret | |
} | |
func (f *FsChecker) Check() (bool, error) { | |
changed := false | |
mod, err := f.GetLastMod() | |
if f.lastMod==nil { | |
f.lastMod = mod | |
} | |
if mod!=nil && f.lastMod!=nil && f.lastMod.Unix() < mod.Unix() { | |
f.lastMod = mod | |
changed = true | |
} | |
return changed, err | |
} | |
func (f *FsChecker) GetLastMod() (*time.Time, error) { | |
var mod *time.Time | |
for _, iPath := range f.inputPaths { | |
err := filepath.Walk(iPath, func(path string, info os.FileInfo, err error) error { | |
if err == nil { | |
if mod==nil { | |
k := info.ModTime() | |
mod = &k | |
} else if (*mod).Unix() < info.ModTime().Unix() { | |
k := info.ModTime() | |
mod = &k | |
} | |
} | |
return err | |
}) | |
if err!=nil { | |
return mod, err | |
} | |
} | |
return mod, nil | |
} | |
func WatchThis (p ProducerFunc, c CheckerFunc, d DeliverStrategy, input ...string) (<-chan bool, chan chan ProducedResult) { | |
done := make(chan bool) | |
done1 := make(chan bool) | |
done2 := make(chan bool) | |
need := make(chan chan ProducedResult) | |
updates := make(chan ProducedResult) | |
go func () { | |
for { | |
select { | |
case <-done2: | |
return | |
default: | |
changed, checkErr := c() | |
if changed && checkErr==nil { | |
r, produceErr := p(input...) | |
updates<-ProducedResult{ | |
Result: r, | |
Err: produceErr, | |
} | |
} | |
<-time.After(time.Second) | |
} | |
} | |
}() | |
go func () { | |
result, err := p(input...) | |
for { | |
select { | |
case <-done1: | |
return | |
case producedResult := <-updates: | |
result = producedResult.Result | |
err = producedResult.Err | |
case res:=<-need: | |
d(res, ProducedResult{ | |
Result: result, | |
Err: err, | |
}) | |
default: | |
// only for dev, because i don t want to heat my computer | |
<-time.After(time.Microsecond*10) | |
} | |
} | |
}() | |
go func () { | |
<-done | |
done1<-true | |
done2<-true | |
}() | |
return done, need | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment