diff --git a/app.go b/app.go index 119148c..fefd957 100644 --- a/app.go +++ b/app.go @@ -48,8 +48,8 @@ type App struct { globals Globals beforeRender middleware.Middleware - template map[string]*tmpl - templateFunc []template.FuncMap + template map[string]*tmpl + templateFuncs []template.FuncMap gracefulShutdown *gracefulShutdown } diff --git a/app_internal_test.go b/app_internal_test.go index 3a9ea88..f4b7925 100644 --- a/app_internal_test.go +++ b/app_internal_test.go @@ -1,7 +1,12 @@ package hime import ( + "crypto/tls" + "html/template" + "log" + "net" "net/http" + "os" "reflect" "testing" "time" @@ -23,18 +28,16 @@ func TestApp(t *testing.T) { assert.Equal(t, reflect.ValueOf(m).Pointer(), reflect.ValueOf(app.beforeRender).Pointer()) }) - t.Run("Routes", func(t *testing.T) { - app.Routes(Routes{"a": "/b", "b": "/cd"}) - assert.Len(t, app.routes, 2) - assert.Equal(t, "/b", app.Route("a")) - assert.Equal(t, "/cd", app.Route("b")) - }) + t.Run("TemplateFuncs", func(t *testing.T) { + app.TemplateFunc("a", func() {}) + assert.Len(t, app.templateFuncs, 1) - t.Run("Globals", func(t *testing.T) { - app.Globals(Globals{"a": 12, "b": "34"}) - assert.Len(t, app.globals, 2) - assert.Equal(t, 12, app.Global("a")) - assert.Equal(t, "34", app.Global("b")) + app.TemplateFuncs(template.FuncMap{ + "a": func() {}, + "b": func() {}, + "c": func() {}, + }) + assert.Len(t, app.templateFuncs, 2) }) t.Run("GracefulShutdown", func(t *testing.T) { @@ -59,3 +62,24 @@ func TestApp(t *testing.T) { assert.Len(t, gs.notiFns, 3) }) } + +func TestConfigServer(t *testing.T) { + t.Parallel() + + app := &App{ + TLSConfig: &tls.Config{}, + ReadTimeout: 5 * time.Second, + ReadHeaderTimeout: 6 * time.Second, + WriteTimeout: 7 * time.Second, + IdleTimeout: 2 * time.Minute, + MaxHeaderBytes: 1024, + TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){}, + ConnState: func(net.Conn, http.ConnState) {}, + ErrorLog: log.New(os.Stderr, "", log.LstdFlags), + } + + assert.Empty(t, &app.srv) + app.configServer(":8080") + assert.NotEmpty(t, &app.srv) + assert.Equal(t, ":8080", app.srv.Addr) +} diff --git a/config.go b/config.go index 4f6239d..e644485 100644 --- a/config.go +++ b/config.go @@ -7,19 +7,12 @@ import ( yaml "gopkg.in/yaml.v2" ) -// Config is app's config -type Config struct { +// AppConfig is hime app's config +type AppConfig struct { Globals map[interface{}]interface{} `yaml:"globals" json:"globals"` Routes map[string]string `yaml:"routes" json:"routes"` - Templates []struct { - Dir string `yaml:"dir" json:"dir"` - Root string `yaml:"root" json:"root"` - Minify bool `yaml:"minify" json:"minify"` - Components []string `yaml:"components" json:"components"` - List map[string][]string `yaml:"list" json:"list"` - Delims []string `yaml:"delims" json:"delims"` - } `yaml:"templates" json:"templates"` - Server struct { + Templates []TemplateConfig `yaml:"templates" json:"templates"` + Server struct { ReadTimeout string `yaml:"readTimeout" json:"readTimeout"` ReadHeaderTimeout string `yaml:"readHeaderTimeout" json:"readHeaderTimeout"` WriteTimeout string `yaml:"writeTimeout" json:"writeTimeout"` @@ -72,24 +65,12 @@ func parseDuration(s string, t *time.Duration) { // gracefulShutdown: // timeout: 1m // wait: 5s -func (app *App) Config(config Config) *App { +func (app *App) Config(config AppConfig) *App { app.Globals(config.Globals) app.Routes(config.Routes) for _, cfg := range config.Templates { - tp := app.Template() - tp.Dir(cfg.Dir) - tp.Root(cfg.Root) - if len(cfg.Delims) == 2 { - tp.Delims(cfg.Delims[0], cfg.Delims[1]) - } - tp.Component(cfg.Components...) - for name, filenames := range cfg.List { - tp.Parse(name, filenames...) - } - if cfg.Minify { - tp.Minify() - } + app.Template().Config(cfg) } // load server config @@ -112,12 +93,11 @@ func (app *App) Config(config Config) *App { // ParseConfig parses config data func (app *App) ParseConfig(data []byte) *App { - var config Config + var config AppConfig err := yaml.Unmarshal(data, &config) if err != nil { panic(err) } - return app.Config(config) } diff --git a/request_internal_test.go b/request_internal_test.go new file mode 100644 index 0000000..e172905 --- /dev/null +++ b/request_internal_test.go @@ -0,0 +1,26 @@ +package hime + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTrimComma(t *testing.T) { + t.Parallel() + + cases := []struct { + Input string + Output string + }{ + {"", ""}, + {"123", "123"}, + {"12,345", "12345"}, + {" 12, ,, 34,5, ,,", " 12 345 "}, + {"12,345.67", "12345.67"}, + } + + for _, c := range cases { + assert.Equal(t, c.Output, trimComma(c.Input)) + } +} diff --git a/route_internal_test.go b/route_internal_test.go new file mode 100644 index 0000000..c2c2cf0 --- /dev/null +++ b/route_internal_test.go @@ -0,0 +1,24 @@ +package hime + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRoute(t *testing.T) { + t.Parallel() + + app := New() + assert.Panics(t, func() { app.Route("empty") }) + + app.Routes(Routes{"a": "/b", "b": "/cd"}) + assert.Len(t, app.routes, 2) + assert.Equal(t, "/b", app.Route("a")) + assert.Equal(t, "/cd", app.Route("b")) + + assert.Panics(t, func() { app.Route("notexists") }) + + ctx := appContext{app: app} + assert.Equal(t, "/b", ctx.Route("a")) +} diff --git a/template.go b/template.go index 3d942ab..a01ab7c 100644 --- a/template.go +++ b/template.go @@ -4,14 +4,26 @@ import ( "bytes" "html/template" "io" + "io/ioutil" "path/filepath" "github.com/tdewolff/minify" "github.com/tdewolff/minify/css" "github.com/tdewolff/minify/html" "github.com/tdewolff/minify/js" + yaml "gopkg.in/yaml.v2" ) +// TemplateConfig is template config +type TemplateConfig struct { + Dir string `yaml:"dir" json:"dir"` + Root string `yaml:"root" json:"root"` + Minify bool `yaml:"minify" json:"minify"` + Components []string `yaml:"components" json:"components"` + List map[string][]string `yaml:"list" json:"list"` + Delims []string `yaml:"delims" json:"delims"` +} + // Template creates new template loader func (app *App) Template() *Template { if app.template == nil { @@ -22,16 +34,21 @@ func (app *App) Template() *Template { funcs: append([]template.FuncMap{template.FuncMap{ "route": app.Route, "global": app.Global, - }}, app.templateFunc...), + }}, app.templateFuncs...), } } // TemplateFuncs registers app's level template funcs func (app *App) TemplateFuncs(funcs ...template.FuncMap) *App { - app.templateFunc = append(app.templateFunc, funcs...) + app.templateFuncs = append(app.templateFuncs, funcs...) return app } +// TemplateFunc registers an app's level template func +func (app *App) TemplateFunc(name string, f interface{}) *App { + return app.TemplateFuncs(template.FuncMap{name: f}) +} + type tmpl struct { template.Template m *minify.M @@ -65,6 +82,43 @@ type Template struct { minifier *minify.M } +// Config loads template config +func (tp *Template) Config(cfg TemplateConfig) *Template { + tp.Dir(cfg.Dir) + tp.Root(cfg.Root) + if len(cfg.Delims) == 2 { + tp.Delims(cfg.Delims[0], cfg.Delims[1]) + } + tp.Component(cfg.Components...) + for name, filenames := range cfg.List { + tp.Parse(name, filenames...) + } + if cfg.Minify { + tp.Minify() + } + + return tp +} + +// ParseConfig parses template config data +func (tp *Template) ParseConfig(data []byte) *Template { + var config TemplateConfig + err := yaml.Unmarshal(data, &config) + if err != nil { + panic(err) + } + return tp.Config(config) +} + +// ParseConfigFile parses template config from file +func (tp *Template) ParseConfigFile(filename string) *Template { + data, err := ioutil.ReadFile(filename) + if err != nil { + panic(err) + } + return tp.ParseConfig(data) +} + // Minify enables minify when render html, css, js func (tp *Template) Minify() *Template { tp.minifier = minify.New() @@ -110,6 +164,11 @@ func (tp *Template) Funcs(funcs ...template.FuncMap) *Template { return tp } +// Func adds a template func while load template +func (tp *Template) Func(name string, f interface{}) *Template { + return tp.Funcs(template.FuncMap{name: f}) +} + // Component adds given templates to every templates func (tp *Template) Component(filename ...string) *Template { tp.components = append(tp.components, filename...)