diff --git a/main.go b/main.go index a0e9e2d..a28f351 100644 --- a/main.go +++ b/main.go @@ -15,41 +15,36 @@ package main import ( - "github.com/gin-gonic/gin" - "github.com/Sirupsen/logrus" + "flag" + "net/http" "runtime" "time" - "flag" + "amuz.es/go/mnemonics/route" "amuz.es/go/mnemonics/util" - "net/http" - "github.com/flosch/pongo2" - "os" + "github.com/gin-gonic/gin" ) var argIp = flag.String("listen_ip", "", "IP to listen on, defaults to all IPs") var argPort = flag.Int("port", 8080, "port to listen") var maxProcs = flag.Int("max_procs", 0, "max number of CPUs that can be used simultaneously. Less than 1 for default (number of cores).") -var logger = logrus.New() func main() { flag.Parse() setMaxProcs() + util.InitLogger() + + gin.SetMode(gin.ReleaseMode) r := gin.New() - - - r.Use(util.Ginrus(logrus.StandardLogger(), time.RFC3339, true)) - + // middleware settings + r.Use(util.Ginrus(time.RFC3339, true)) r.Use(gin.Recovery()) + // not found handler r.NoRoute(func(c *gin.Context) { - c.HTML(http.StatusOK, "page_404.html", pongo2.Context{}) + c.HTML(http.StatusOK, "page_404.html", util.Context{}) }) - - logger.Level = logrus.ErrorLevel - logger.Out=os.Stderr - r.Use(util.Ginrus(logger, time.RFC3339, false)) r.StaticFS("/static", route.Static("")) r.GET("/api", route.Api) @@ -59,12 +54,15 @@ func main() { c.String(200, "OK! - mnemonics services are fully operational!") }) - // Use pongo2gin.Default() for default options or pongo2gin.New() - // if you need to use custom RenderOptions. - r.HTMLRender = util.Default() + if gin.IsDebugging() { + r.HTMLRender = util.TemplateSourceDebug("asset/template") + } else { + r.HTMLRender = util.TemplateSourceRelease("") + } + r.GET("/", route.Index) - logger.Error("bootstrapped application") + util.Log().Error("bootstrapped application") r.Run() // listen and server on 0.0.0.0:8080 } @@ -83,6 +81,6 @@ func setMaxProcs() { // Check if the setting was successful. actualNumProcs := runtime.GOMAXPROCS(0) if actualNumProcs != numProcs { - logger.Warnf("Specified max procs of %v but using %v", numProcs, actualNumProcs) + util.Log().Warnf("Specified max procs of %v but using %v", numProcs, actualNumProcs) } } diff --git a/route/index.go b/route/index.go index d952f67..9670172 100644 --- a/route/index.go +++ b/route/index.go @@ -2,11 +2,12 @@ package route import ( "github.com/gin-gonic/gin" - "github.com/flosch/pongo2" "net/http" + + "amuz.es/go/mnemonics/util" ) func Index(c *gin.Context) { - c.HTML(http.StatusOK, "index.html", pongo2.Context{}) + c.HTML(http.StatusOK, "index.html", util.Context{}) } diff --git a/util/ginrus.go b/util/logger.go similarity index 82% rename from util/ginrus.go rename to util/logger.go index 66abafb..d48827a 100644 --- a/util/ginrus.go +++ b/util/logger.go @@ -6,10 +6,25 @@ package util import ( "time" + "os" + "github.com/Sirupsen/logrus" "github.com/gin-gonic/gin" ) +var logger = logrus.New() + +func InitLogger() { + // logging framework + logger.Level = logrus.ErrorLevel + logger.Out = os.Stderr +} + +func Log() *logrus.Logger { + // logging framework + return logger +} + // Ginrus returns a gin.HandlerFunc (middleware) that logs requests using logrus. // // Requests with errors are logged using logrus.Error(). @@ -18,7 +33,7 @@ import ( // It receives: // 1. A time package format string (e.g. time.RFC3339). // 2. A boolean stating whether to use UTC time zone or local. -func Ginrus(logger *logrus.Logger, timeFormat string, utc bool) gin.HandlerFunc { +func Ginrus(timeFormat string, utc bool) gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() // some evil middlewares modify this values diff --git a/util/renderer.go b/util/renderer.go index 9685fb2..9b9ac7a 100644 --- a/util/renderer.go +++ b/util/renderer.go @@ -2,70 +2,99 @@ package util import ( "net/http" + "path" + + "bytes" + "io" + "path/filepath" + "amuz.es/go/mnemonics/bind/template" "github.com/flosch/pongo2" "github.com/gin-gonic/gin/render" ) -// RenderOptions is used to configure the renderer. -type RenderOptions struct { - ContentType string +type ( + PongoRelease struct { + templateSet *pongo2.TemplateSet + Path string + } + + PongoDebug struct { + Path string + } + + Pongo struct { + Template *pongo2.Template + Name string + Data interface{} + } + + Context pongo2.Context +) + +func TemplateSourceRelease(path string) *PongoRelease { + ts := pongo2.NewSet(path, &memoryTemplateLoader{loaderFunc: template.Asset}) + return &PongoRelease{ts, path} } -// Pongo2Render is a custom Gin template renderer using Pongo2. -type Pongo2Render struct { - Options *RenderOptions - Template *pongo2.Template - Context pongo2.Context +func (p PongoRelease) Instance(name string, data interface{}) render.Render { + t := pongo2.Must(p.templateSet.FromFile(path.Join(p.Path, name))) + + return Pongo{ + Template: t, + Name: name, + Data: data, + } +} +func TemplateSourceDebug(path string) *PongoDebug { + return &PongoDebug{path} } -// New creates a new Pongo2Render instance with custom Options. -func New(options RenderOptions) *Pongo2Render { - return &Pongo2Render{ - Options: &options, +func (p PongoDebug) Instance(name string, data interface{}) render.Render { + + t := pongo2.Must(pongo2.FromFile(path.Join(p.Path, name))) + return Pongo{ + Template: t, + Name: name, + Data: data, } } -// Default creates a Pongo2Render instance with default options. -func Default() *Pongo2Render { - return New(RenderOptions{ - ContentType: "text/html; charset=utf-8", - }) +func (p Pongo) Render(w http.ResponseWriter) error { + ctx := pongo2.Context(p.Data.(Context)) + w.Header().Set("Content-Type", "text/html; charset=utf-8") + return p.Template.ExecuteWriter(ctx, w) } -// Instance should return a new Pongo2Render struct per request and prepare -// the template by either loading it from disk or using pongo2's cache. -func (p Pongo2Render) Instance(name string, data interface{}) render.Render { - var templateResult *pongo2.Template - // always read template files from disk if in debug mode, use cache otherwise. - //if gin.Mode() == "debug" {Render - // template = pongo2.Must(pongo2.FromFile(filename)) - //} else { - // template = pongo2.Must(pongo2.FromCache(filename)) - //} - templateContent:=template.MustAsset(name) +type memoryTemplateLoader struct { + loaderFunc func(path string) ([]byte, error) + fallbackLoader *pongo2.TemplateLoader +} - templateResult=pongo2.Must(pongo2.FromString(string(templateContent[:]))) - - return Pongo2Render{ - Template: templateResult, - Context: data.(pongo2.Context), - Options: p.Options, +func (m memoryTemplateLoader) Abs(base, name string) string { + if filepath.IsAbs(name) || base == "" { + return name } -} -// Render should render the template to the response. -func (p Pongo2Render) Render(w http.ResponseWriter) error { - writeContentType(w, []string{p.Options.ContentType}) - err := p.Template.ExecuteWriter(p.Context, w) - return err -} - -// writeContentType is also in the gin/render package but it has not been made -// pubic so is repeated here, maybe convince the author to make this public. -func writeContentType(w http.ResponseWriter, value []string) { - header := w.Header() - if val := header["Content-Type"]; len(val) == 0 { - header["Content-Type"] = value + if name == "" { + return base } + + return filepath.Dir(base) + string(filepath.Separator) + name +} + +func (m memoryTemplateLoader) Get(path string) (io.Reader, error) { + + Log().Errorf("hello template %s", filepath.Clean(path)) + // t := pongo2.Must(p.templateSet.FromFile(path.Join(p.Path, name))) + data, err := m.loaderFunc(path) + if err != nil { + if m.fallbackLoader != nil { + return (*m.fallbackLoader).Get(path) + } + + return nil, err + } + + return bytes.NewReader(data), nil }