Skip to content

Instantly share code, notes, and snippets.

@mh-cbon
Last active October 4, 2016 11:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mh-cbon/9eb7701e57a8f37bb7e62647dc9726e8 to your computer and use it in GitHub Desktop.
Save mh-cbon/9eb7701e57a8f37bb7e62647dc9726e8 to your computer and use it in GitHub Desktop.
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)
}
}
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...)
}
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