From aa319fd8e474a5c64b4ae46cea12ce4f26e98a93 Mon Sep 17 00:00:00 2001 From: Makis Maropoulos Date: Tue, 28 Jun 2016 12:50:26 +0300 Subject: [PATCH] Handlebars support - Embrace the feature request https://github.com/kataras/iris/issues/239 --- README.md | 6 +- THIRDPARTY-LICENSE.md | 28 ++++- config/render.go | 12 ++ initiatory.go | 8 +- iris.go | 2 +- .../template/engine/handlebars/handlebars.go | 110 ++++++++++++++++++ render/template/template.go | 4 + 7 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 render/template/engine/handlebars/handlebars.go diff --git a/README.md b/README.md index 22a08fcb..3f1d50c4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [Travis]: http://travis-ci.org/kataras/iris [License Widget]: https://img.shields.io/badge/license-MIT%20%20License%20-E91E63.svg?style=flat-square [License]: https://github.com/kataras/iris/blob/master/LICENSE -[Release Widget]: https://img.shields.io/badge/release-v3.0.0--rc.3-blue.svg?style=flat-square +[Release Widget]: https://img.shields.io/badge/release-v3.0.0--rc.4-blue.svg?style=flat-square [Release]: https://github.com/kataras/iris/releases [Gitter Widget]: https://img.shields.io/badge/chat-on%20gitter-00BCD4.svg?style=flat-square [Gitter]: https://gitter.im/kataras/iris @@ -72,7 +72,7 @@ Features ------------ - Focus on high performance - Robust routing supports static and wildcard subdomains -- View system supporting [5+](https://kataras.gitbooks.io/iris/content/render_templates.html) template engines +- View system supporting [6+](https://kataras.gitbooks.io/iris/content/render_templates.html) template engines - Highly scalable Websocket API with custom events - Sessions support with GC, memory & redis providers - Middlewares & Plugins were never be easier @@ -151,7 +151,7 @@ I recommend writing your API tests using this new library, [httpexpect](https:// Versioning ------------ -Current: **v3.0.0-rc.3** +Current: **v3.0.0-rc.4** > Iris is an active project diff --git a/THIRDPARTY-LICENSE.md b/THIRDPARTY-LICENSE.md index 4d8c4cd1..0e72635d 100644 --- a/THIRDPARTY-LICENSE.md +++ b/THIRDPARTY-LICENSE.md @@ -2,7 +2,8 @@ Third party packages ------------ - [Iris is build on top of fasthttp](https://github.com/valyala/fasthttp) -- [pongo2 is one of the supporting template engines](https://github.com/flosch/pongo2) +- [django is one of the supporting template engines](https://github.com/flosch/pongo2) +- [handlebars is one of the supporting template engines](https://github.com/aymerick/raymond) - [amber is one of the supporting template engines](https://github.com/eknkc/amber) - [jade is one of the supporting template engines](https://github.com/Joker/jade) - [blackfriday is one of the supporting template engines](https://github.com/russross/blackfriday) @@ -42,7 +43,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -pongo2 - The MIT License (MIT) +django - The MIT License (MIT) Copyright (c) 2013-2014 Florian Schlachter @@ -63,6 +64,29 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +handlebars - The MIT License (MIT) + +Copyright (c) 2015 Aymerick JEHANNE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + Jade - Copyright (c) 2015, Joker All rights reserved. diff --git a/config/render.go b/config/render.go index e15806d7..ab180e55 100644 --- a/config/render.go +++ b/config/render.go @@ -26,6 +26,9 @@ const ( // AmberEngine is a Template's config for engine type // when use this, the templates are eknkc/amber AmberEngine EngineType = 4 + // HandlebarsEngine is a Template's config for engine type + // when use this, the templates are aymerick/raymond + HandlebarsEngine EngineType = 5 // DefaultEngine is the HTMLEngine DefaultEngine EngineType = HTMLEngine @@ -115,6 +118,8 @@ type ( Markdown Markdown // Amber contains specific configs for amber Amber Amber + // Handlebars contains specific configs for handlebars + Handlebars Handlebars } // HTMLTemplate the configs for HTMLEngine @@ -154,6 +159,12 @@ type ( // Funcs for the html/template result, amber default funcs are not overrided so use it without worries Funcs template.FuncMap } + // Handlebars the configs for HandlebarsEngine + Handlebars struct { + // Helpers for Handlebars, you can register your own by raymond.RegisterHelper(name string, a interface{}) or RegisterHelpers(map[string]interface{}) + // or just fill this method, do not override it it is not nil by default (because of Iris' helpers (url and urlpath) + Helpers map[string]interface{} + } ) // DefaultRest returns the default config for rest @@ -210,6 +221,7 @@ func DefaultTemplate() Template { Pongo: Pongo{Filters: make(map[string]pongo2.FilterFunction, 0), Globals: make(map[string]interface{}, 0)}, Markdown: Markdown{Sanitize: false}, Amber: Amber{Funcs: template.FuncMap{}}, + Handlebars: Handlebars{Helpers: make(map[string]interface{}, 0)}, } } diff --git a/initiatory.go b/initiatory.go index 26bf1791..80d58495 100644 --- a/initiatory.go +++ b/initiatory.go @@ -8,11 +8,10 @@ import ( "github.com/kataras/iris/config" "github.com/kataras/iris/logger" - "github.com/kataras/iris/websocket" - "github.com/kataras/iris/render/rest" "github.com/kataras/iris/render/template" "github.com/kataras/iris/sessions" + "github.com/kataras/iris/websocket" ///NOTE: register the session providers, but the s.Config.Sessions.Provider will be used only, if this empty then sessions are disabled. _ "github.com/kataras/iris/sessions/providers/memory" _ "github.com/kataras/iris/sessions/providers/redis" @@ -39,7 +38,6 @@ func init() { const ( /* conversional */ - // HTMLEngine conversion for config.HTMLEngine HTMLEngine = config.HTMLEngine // PongoEngine conversion for config.PongoEngine @@ -50,7 +48,8 @@ const ( JadeEngine = config.JadeEngine // AmberEngine conversion for config.AmberEngine AmberEngine = config.AmberEngine - + // HandlebarsEngine conversion for config.HandlebarsEngine + HandlebarsEngine = config.HandlebarsEngine // DefaultEngine conversion for config.DefaultEngine DefaultEngine = config.DefaultEngine // NoEngine conversion for config.NoEngine @@ -58,7 +57,6 @@ const ( // NoLayout to disable layout for a particular template file // conversion for config.NoLayout NoLayout = config.NoLayout - /* end conversional */ ) diff --git a/iris.go b/iris.go index 3d86e40e..62853a22 100644 --- a/iris.go +++ b/iris.go @@ -68,7 +68,7 @@ import ( const ( // Version of the iris - Version = "3.0.0-rc.3" + Version = "3.0.0-rc.4" banner = ` _____ _ |_ _| (_) | | ____ _ ___ diff --git a/render/template/engine/handlebars/handlebars.go b/render/template/engine/handlebars/handlebars.go new file mode 100644 index 00000000..5e86dee5 --- /dev/null +++ b/render/template/engine/handlebars/handlebars.go @@ -0,0 +1,110 @@ +// Package handlebars the HandlebarsEngine's functionality +package handlebars + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "sync" + + "github.com/aymerick/raymond" + "github.com/kataras/iris/config" +) + +type ( + // Engine the Handlebars engine + Engine struct { + Config *config.Template + templateCache map[string]*raymond.Template + mu sync.Mutex + } +) + +// New creates and returns the Handlebars template engine +func New(c config.Template) *Engine { + s := &Engine{Config: &c, templateCache: make(map[string]*raymond.Template, 0)} + return s +} + +// BuildTemplates builds the handlebars templates +func (e *Engine) BuildTemplates() error { + if e.Config.Extensions == nil || len(e.Config.Extensions) == 0 { + e.Config.Extensions = []string{".html"} + } + + // register the global helpers + if e.Config.Handlebars.Helpers != nil { + raymond.RegisterHelpers(e.Config.Handlebars.Helpers) + } + var templateErr error + + dir := e.Config.Directory + filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if info == nil || info.IsDir() { + return nil + } + + rel, err := filepath.Rel(dir, path) + if err != nil { + return err + } + + ext := "" + if strings.Index(rel, ".") != -1 { + ext = filepath.Ext(rel) + } + + for _, extension := range e.Config.Extensions { + if ext == extension { + + buf, err := ioutil.ReadFile(path) + contents := string(buf) + + if err != nil { + templateErr = err + break + } + + name := filepath.ToSlash(rel) + + tmpl, err := raymond.Parse(contents) + if err != nil { + templateErr = err + continue + } + e.mu.Lock() + e.templateCache[name] = tmpl + e.mu.Unlock() + + break + } + } + return nil + }) + return templateErr + +} +func (e *Engine) fromCache(relativeName string) *raymond.Template { + e.mu.Lock() + tmpl, ok := e.templateCache[relativeName] + if ok { + e.mu.Unlock() + return tmpl + } + e.mu.Unlock() + return nil +} + +// ExecuteWriter executes a templates and write its results to the out writer +func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error { + if tmpl := e.fromCache(name); tmpl != nil { + res, err := tmpl.Exec(binding) + _, err = fmt.Fprint(out, res) + return err + } + + return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, e.Config.Directory) +} diff --git a/render/template/template.go b/render/template/template.go index 41c5cd20..0ce3db94 100644 --- a/render/template/template.go +++ b/render/template/template.go @@ -11,6 +11,7 @@ import ( "github.com/kataras/iris/config" "github.com/kataras/iris/context" "github.com/kataras/iris/render/template/engine/amber" + "github.com/kataras/iris/render/template/engine/handlebars" "github.com/kataras/iris/render/template/engine/html" "github.com/kataras/iris/render/template/engine/jade" "github.com/kataras/iris/render/template/engine/markdown" @@ -98,6 +99,9 @@ func New(c config.Template) *Template { case config.AmberEngine: setSharedFuncs(sharedFuncs, c.Amber.Funcs) e = amber.New(c) // Amber + case config.HandlebarsEngine: + setSharedFuncs(sharedFuncs, c.Handlebars.Helpers) + e = handlebars.New(c) default: // config.NoEngine return nil }