diff --git a/HISTORY.md b/HISTORY.md
index 523ce10a..cbcc2789 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -359,6 +359,8 @@ Response:
Other Improvements:
+- Add [Ace](_examples/view/template_ace_0) template parser to the view engine and other minor improvements.
+
- Fix huge repo size of 55.7MB, which slows down the overall Iris installation experience. Now, go-get performs ~3 times faster. I 've managed it using the [bfg-repo-cleaner](https://github.com/rtyley/bfg-repo-cleaner) tool - an alternative to git-filter-branch command. Watch the small gif below to learn how:
[![](https://media.giphy.com/media/U8560aiWTurW4iAOLn/giphy.gif)](https://media.giphy.com/media/U8560aiWTurW4iAOLn/giphy.gif)
diff --git a/NOTICE b/NOTICE
index 753c1e64..c7754fd4 100644
--- a/NOTICE
+++ b/NOTICE
@@ -10,7 +10,10 @@ Revision ID: d1c07411df0bb21f6b21f5b5d9325fac6f29c911
----------------- ----------------- ------------------------------------------
Package Version Website
------------------ ----------------- ------------------------------------------
+----------------- ----------------- ------------------------------------------
+ ace ea038f4770b6746 https://github.com/yosssi/ace
+ c3f8f84f14fa60d
+ 9fe1205b56
badger 536fed1846d0f4d https://github.com/dgraph-io/badger
b9579bcff679761
4e134eadfa
diff --git a/_examples/README.md b/_examples/README.md
index b61c2b7f..1cafd46b 100644
--- a/_examples/README.md
+++ b/_examples/README.md
@@ -111,6 +111,7 @@
* [Jet Embedded](view/template_jet_1_embedded)
* [Jet 'urlpath' tmpl func](view/template_jet_2)
* [Jet Template Funcs from Struct](view/template_jet_3)
+ - [Ace](view/template_ace_0)
* Third-Parties
* [Render `valyala/quicktemplate` templates](view/quicktemplate)
* [Render `shiyanhui/hero` templates](view/herotemplate)
diff --git a/_examples/view/template_ace_0/main.go b/_examples/view/template_ace_0/main.go
new file mode 100644
index 00000000..fcf6619b
--- /dev/null
+++ b/_examples/view/template_ace_0/main.go
@@ -0,0 +1,31 @@
+package main
+
+import "github.com/kataras/iris/v12"
+
+func main() {
+ app := iris.New()
+
+ // Read about its markup syntax at: https://github.com/yosssi/ace
+ tmpl := iris.Ace("./views", ".ace")
+ // tmpl.Layout("layouts/main.ace") -> global layout for all pages.
+
+ app.RegisterView(tmpl)
+
+ app.Get("/", func(ctx iris.Context) {
+ ctx.View("index.ace", iris.Map{
+ "Title": "Title of The Page",
+ })
+ })
+
+ app.Get("/layout", func(ctx iris.Context) {
+ ctx.ViewLayout("layouts/main.ace") // layout for that response.
+ ctx.View("index.ace", iris.Map{
+ "Title": "Title of the main Page",
+ })
+ })
+
+ // otherGroup := app.Party("/other").Layout("layouts/other.ace") -> layout for that party.
+ // otherGroup.Get("/", func(ctx iris.Context) { ctx.View("index.ace", [...]) })
+
+ app.Listen(":8080")
+}
diff --git a/_examples/view/template_ace_0/views/index.ace b/_examples/view/template_ace_0/views/index.ace
new file mode 100644
index 00000000..776cc11f
--- /dev/null
+++ b/_examples/view/template_ace_0/views/index.ace
@@ -0,0 +1,5 @@
+= include partials/header.ace .
+
+h2 {{.Title}}
+
+= include partials/footer.ace .
\ No newline at end of file
diff --git a/_examples/view/template_ace_0/views/layouts/main.ace b/_examples/view/template_ace_0/views/layouts/main.ace
new file mode 100644
index 00000000..b3997c7c
--- /dev/null
+++ b/_examples/view/template_ace_0/views/layouts/main.ace
@@ -0,0 +1,6 @@
+= doctype html
+html
+ head
+ title Main Page
+ body
+ {{ yield }}
\ No newline at end of file
diff --git a/_examples/view/template_ace_0/views/partials/footer.ace b/_examples/view/template_ace_0/views/partials/footer.ace
new file mode 100644
index 00000000..99261685
--- /dev/null
+++ b/_examples/view/template_ace_0/views/partials/footer.ace
@@ -0,0 +1 @@
+h1 Partial Footer
\ No newline at end of file
diff --git a/_examples/view/template_ace_0/views/partials/header.ace b/_examples/view/template_ace_0/views/partials/header.ace
new file mode 100644
index 00000000..dca23ac2
--- /dev/null
+++ b/_examples/view/template_ace_0/views/partials/header.ace
@@ -0,0 +1 @@
+h1 Partial Header
\ No newline at end of file
diff --git a/_examples/view/template_html_2/README.md b/_examples/view/template_html_2/README.md
index b987e79c..f4f5ac99 100644
--- a/_examples/view/template_html_2/README.md
+++ b/_examples/view/template_html_2/README.md
@@ -1,3 +1,3 @@
## Info
-This folder examines the {{render "dir/templatefilename"}} functionality to manually render any template inside any template
+This folder examines the {{render "dir/templatefilename" .}} functionality to manually render any template inside any template
diff --git a/_examples/view/template_html_2/templates/page1.html b/_examples/view/template_html_2/templates/page1.html
index 22bd16a1..8c1d8ed6 100644
--- a/_examples/view/template_html_2/templates/page1.html
+++ b/_examples/view/template_html_2/templates/page1.html
@@ -2,6 +2,6 @@
Page 1 {{ greet "iris developer"}}
- {{ render "partials/page1_partial1.html"}}
+ {{ render "partials/page1_partial1.html" }}
diff --git a/aliases.go b/aliases.go
index d0126d80..57d2945a 100644
--- a/aliases.go
+++ b/aliases.go
@@ -207,6 +207,9 @@ var (
// Jet view engine.
// Shortcut of the kataras/iris/view.Jet.
Jet = view.Jet
+ // Ace view engine.
+ // Shortcut of the kataras/iris/view.Ace.
+ Ace = view.Ace
)
// PrefixDir returns a new FileSystem that opens files
diff --git a/configuration.go b/configuration.go
index 596cb541..5906a309 100644
--- a/configuration.go
+++ b/configuration.go
@@ -81,7 +81,7 @@ func parseYAML(filename string) (Configuration, error) {
//
// Accepts the absolute path of the cfg.yml.
// An error will be shown to the user via panic with the error message.
-// Error may occur when the cfg.yml doesn't exists or is not formatted correctly.
+// Error may occur when the cfg.yml does not exist or is not formatted correctly.
//
// Note: if the char '~' passed as "filename" then it tries to load and return
// the configuration from the $home_directory + iris.yml,
@@ -115,7 +115,7 @@ func YAML(filename string) Configuration {
//
// Accepts the absolute path of the configuration file.
// An error will be shown to the user via panic with the error message.
-// Error may occur when the file doesn't exists or is not formatted correctly.
+// Error may occur when the file does not exist or is not formatted correctly.
//
// Note: if the char '~' passed as "filename" then it tries to load and return
// the configuration from the $home_directory + iris.tml,
diff --git a/go.mod b/go.mod
index 9d92bccc..883aec67 100644
--- a/go.mod
+++ b/go.mod
@@ -33,10 +33,11 @@ require (
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693
github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1
+ github.com/yosssi/ace v0.0.5
go.etcd.io/bbolt v1.3.5
- golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
+ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
golang.org/x/net v0.0.0-20200707034311-ab3426394381
- golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae
+ golang.org/x/sys v0.0.0-20200802091954-4b90ce9b60b3
golang.org/x/text v0.3.3
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
google.golang.org/protobuf v1.25.0
diff --git a/view/README.md b/view/README.md
index 1617c714..0ff73293 100644
--- a/view/README.md
+++ b/view/README.md
@@ -1,7 +1,7 @@
# View
-Iris supports 6 template engines out-of-the-box, developers can still use any external golang template engine,
-as `context/context#ResponseWriter()` is an `io.Writer`.
+Iris supports 7 template engines out-of-the-box, developers can still use any external golang template engine,
+as `Context.ResponseWriter()` is an `io.Writer`.
All of these six template engines have common features with common API,
like Layout, Template Funcs, Party-specific layout, partial rendering and more.
@@ -12,6 +12,7 @@ like Layout, Template Funcs, Party-specific layout, partial rendering and more.
- Handlebars, its template parser is the [github.com/aymerick/raymond](https://github.com/aymerick/raymond)
- Amber, its template parser is the [github.com/eknkc/amber](https://github.com/eknkc/amber)
- Jet, its template parser is the [github.com/CloudyKit/jet](https://github.com/CloudyKit/jet)
+- Ace, its template parser is the [github.com/yosssi/ace](https://github.com/yosssi/ace)
## Examples
@@ -27,8 +28,9 @@ like Layout, Template Funcs, Party-specific layout, partial rendering and more.
- [Pug (Jade) Actions`](https://github.com/kataras/iris/blob/master/_examples/view/template_pug_1)
- [Pug (Jade) Includes`](https://github.com/kataras/iris/blob/master/_examples/view/template_pug_2)
- [Pug (Jade) Extends`](https://github.com/kataras/iris/blob/master/_examples/view/template_pug_3)
-- [Jet](https://github.com/kataras/iris/blob/master/_examples/view/template_jet_0) **NEW**
-- [Jet Embedded](https://github.com/kataras/iris/blob/master/_examples/view/template_jet_1_embedded) **NEW**
+- [Jet](https://github.com/kataras/iris/blob/master/_examples/view/template_jet_0)
+- [Jet Embedded](https://github.com/kataras/iris/blob/master/_examples/view/template_jet_1_embedded)
+- [Ace](https://github.com/kataras/iris/blob/master/_examples/view/template_ace_0)
You can serve [quicktemplate](https://github.com/valyala/quicktemplate) files too, simply by using the `context#ResponseWriter`, take a look at the [iris/_examples/view/quicktemplate](https://github.com/kataras/iris/tree/master/_examples/view/quicktemplate) example.
diff --git a/view/ace.go b/view/ace.go
new file mode 100644
index 00000000..134f2cf4
--- /dev/null
+++ b/view/ace.go
@@ -0,0 +1,57 @@
+package view
+
+import (
+ "path"
+ "sync"
+
+ "github.com/yosssi/ace"
+)
+
+// Ace returns a new ace view engine.
+// It shares the same exactly logic with the
+// html view engine, it uses the same exactly configuration.
+//
+// Read more about the Ace Go Parser: https://github.com/yosssi/ace
+func Ace(directory, extension string) *HTMLEngine {
+ s := HTML(directory, extension)
+
+ funcs := make(map[string]interface{}, 0)
+
+ once := new(sync.Once)
+ s.middleware = func(name string, text []byte) (contents string, err error) {
+ once.Do(func() { // on first template parse, all funcs are given.
+ for k, v := range s.funcs {
+ funcs[k] = v
+ }
+ for k, v := range emptyFuncs {
+ funcs[k] = v
+ }
+ })
+
+ name = path.Join(path.Clean(directory), name)
+
+ src := ace.NewSource(
+ ace.NewFile(name, text),
+ ace.NewFile("", []byte{}),
+ []*ace.File{},
+ )
+
+ rslt, err := ace.ParseSource(src, nil)
+ if err != nil {
+ return "", err
+ }
+
+ t, err := ace.CompileResult(name, rslt, &ace.Options{
+ Extension: extension[1:],
+ FuncMap: funcs,
+ DelimLeft: s.left,
+ DelimRight: s.right,
+ })
+ if err != nil {
+ return "", err
+ }
+
+ return t.Lookup(name).Tree.Root.String(), nil
+ }
+ return s
+}
diff --git a/view/amber.go b/view/amber.go
index 05986dd1..c8a7fa7c 100644
--- a/view/amber.go
+++ b/view/amber.go
@@ -220,5 +220,5 @@ func (s *AmberEngine) ExecuteWriter(w io.Writer, filename string, layout string,
return tmpl.Execute(w, bindingData)
}
- return fmt.Errorf("Template with name %s doesn't exists in the dir", filename)
+ return fmt.Errorf("Template with name: %s does not exist in the dir: %s", filename, s.directory)
}
diff --git a/view/django.go b/view/django.go
index 81f85082..2732e86d 100644
--- a/view/django.go
+++ b/view/django.go
@@ -376,5 +376,5 @@ func (s *DjangoEngine) ExecuteWriter(w io.Writer, filename string, layout string
return tmpl.ExecuteWriter(getPongoContext(bindingData), w)
}
- return fmt.Errorf("template with name %s doesn't exists in the dir", filename)
+ return fmt.Errorf("template with name: %s ddoes not exist in the dir: %s", filename, s.directory)
}
diff --git a/view/handlebars.go b/view/handlebars.go
index 23003c1a..f1130b96 100644
--- a/view/handlebars.go
+++ b/view/handlebars.go
@@ -291,5 +291,5 @@ func (s *HandlebarsEngine) ExecuteWriter(w io.Writer, filename string, layout st
return err
}
- return fmt.Errorf("template with name %s[original name = %s] doesn't exists in the dir", renderFilename, filename)
+ return fmt.Errorf("template with name: %s[original name = %s] does not exist in the dir: %s", renderFilename, filename, s.directory)
}
diff --git a/view/html.go b/view/html.go
index c030ad7d..a2ad1fe3 100644
--- a/view/html.go
+++ b/view/html.go
@@ -26,8 +26,8 @@ type HTMLEngine struct {
right string
layout string
rmu sync.RWMutex // locks for layoutFuncs and funcs
- layoutFuncs map[string]interface{}
- funcs map[string]interface{}
+ layoutFuncs template.FuncMap
+ funcs template.FuncMap
//
middleware func(name string, contents []byte) (string, error)
@@ -38,7 +38,7 @@ type HTMLEngine struct {
var _ Engine = (*HTMLEngine)(nil)
var emptyFuncs = template.FuncMap{
- "yield": func() (string, error) {
+ "yield": func(binding interface{}) (string, error) {
return "", fmt.Errorf("yield was called, yet no layout defined")
},
"part": func() (string, error) {
@@ -52,7 +52,8 @@ var emptyFuncs = template.FuncMap{
},
"current": func() (string, error) {
return "", nil
- }, "render": func() (string, error) {
+ },
+ "render": func() (string, error) {
return "", nil
},
}
@@ -70,8 +71,8 @@ func HTML(directory, extension string) *HTMLEngine {
left: "{{",
right: "}}",
layout: "",
- layoutFuncs: make(map[string]interface{}),
- funcs: make(map[string]interface{}),
+ layoutFuncs: make(template.FuncMap),
+ funcs: make(template.FuncMap),
}
return s
@@ -172,10 +173,34 @@ func (s *HTMLEngine) AddLayoutFunc(funcName string, funcBody interface{}) *HTMLE
// - url func(routeName string, args ...string) string
// - urlpath func(routeName string, args ...string) string
// - render func(fullPartialName string) (template.HTML, error).
-func (s *HTMLEngine) AddFunc(funcName string, funcBody interface{}) {
+func (s *HTMLEngine) AddFunc(funcName string, funcBody interface{}) *HTMLEngine {
s.rmu.Lock()
s.funcs[funcName] = funcBody
s.rmu.Unlock()
+
+ return s
+}
+
+// SetFuncs overrides the template funcs with the given "funcMap".
+func (s *HTMLEngine) SetFuncs(funcMap template.FuncMap) *HTMLEngine {
+ s.rmu.Lock()
+ s.funcs = funcMap
+ s.rmu.Unlock()
+
+ return s
+}
+
+// Funcs adds the elements of the argument map to the template's function map.
+// It is legal to overwrite elements of the map. The return
+// value is the template, so calls can be chained.
+func (s *HTMLEngine) Funcs(funcMap template.FuncMap) *HTMLEngine {
+ s.rmu.Lock()
+ for k, v := range funcMap {
+ s.funcs[k] = v
+ }
+ s.rmu.Unlock()
+
+ return s
}
// Load parses the templates to the engine.
@@ -266,6 +291,7 @@ func (s *HTMLEngine) loadDirectory() error {
name := filepath.ToSlash(rel)
tmpl := s.Templates.New(name)
tmpl.Option(s.options...)
+
if s.middleware != nil {
contents, err = s.middleware(name, buf)
}
@@ -275,7 +301,12 @@ func (s *HTMLEngine) loadDirectory() error {
}
// s.mu.Lock()
// Add our funcmaps.
- _, err = tmpl.Funcs(emptyFuncs).Funcs(s.funcs).Parse(contents)
+ _, err = tmpl.
+ Funcs(emptyFuncs).
+ // Funcs(s.makeDefaultLayoutFuncs(name)).
+ // Funcs(s.layoutFuncs).
+ Funcs(s.funcs).
+ Parse(contents)
// s.mu.Unlock()
if err != nil {
templateErr = err
@@ -393,15 +424,28 @@ func (s *HTMLEngine) executeTemplateBuf(name string, binding interface{}) (*byte
return buf, err
}
-func (s *HTMLEngine) layoutFuncsFor(name string, binding interface{}) {
+func (s *HTMLEngine) layoutFuncsFor(lt *template.Template, name string, binding interface{}) {
+ s.runtimeFuncsFor(lt, name, binding)
+
funcs := template.FuncMap{
"yield": func() (template.HTML, error) {
buf, err := s.executeTemplateBuf(name, binding)
// Return safe HTML here since we are rendering our own template.
return template.HTML(buf.String()), err
},
+ }
+
+ for k, v := range s.layoutFuncs {
+ funcs[k] = v
+ }
+
+ lt.Funcs(funcs)
+}
+
+func (s *HTMLEngine) runtimeFuncsFor(t *template.Template, name string, binding interface{}) {
+ funcs := template.FuncMap{
"part": func(partName string) (template.HTML, error) {
- nameTemp := strings.Replace(name, ".html", "", -1)
+ nameTemp := strings.Replace(name, s.extension, "", -1)
fullPartName := fmt.Sprintf("%s-%s", nameTemp, partName)
buf, err := s.executeTemplateBuf(fullPartName, binding)
if err != nil {
@@ -440,25 +484,7 @@ func (s *HTMLEngine) layoutFuncsFor(name string, binding interface{}) {
},
}
- for k, v := range s.layoutFuncs {
- funcs[k] = v
- }
- if tpl := s.Templates.Lookup(name); tpl != nil {
- tpl.Funcs(funcs)
- }
-}
-
-func (s *HTMLEngine) runtimeFuncsFor(name string, binding interface{}) {
- funcs := template.FuncMap{
- "render": func(fullPartialName string) (template.HTML, error) {
- buf, err := s.executeTemplateBuf(fullPartialName, binding)
- return template.HTML(buf.String()), err
- },
- }
-
- if tpl := s.Templates.Lookup(name); tpl != nil {
- tpl.Funcs(funcs)
- }
+ t.Funcs(funcs)
}
// ExecuteWriter executes a template and writes its result to the w writer.
@@ -474,14 +500,21 @@ func (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bind
}
}
- layout = getLayout(layout, s.layout)
+ if layout = getLayout(layout, s.layout); layout != "" {
+ lt := s.Templates.Lookup(layout)
+ if lt == nil {
+ return fmt.Errorf("layout: %s does not exist in the dir: %s", name, s.directory)
+ }
- if layout != "" {
- s.layoutFuncsFor(name, bindingData)
- name = layout
- } else {
- s.runtimeFuncsFor(name, bindingData)
+ s.layoutFuncsFor(lt, name, bindingData)
+ return lt.Execute(w, bindingData)
}
- return s.Templates.ExecuteTemplate(w, name, bindingData)
+ t := s.Templates.Lookup(name)
+ if t == nil {
+ return fmt.Errorf("template: %s does not exist in the dir: %s", name, s.directory)
+ }
+ s.runtimeFuncsFor(t, name, bindingData)
+
+ return t.Execute(w, bindingData)
}
diff --git a/view/pug.go b/view/pug.go
index 0a20aec2..f317efe4 100644
--- a/view/pug.go
+++ b/view/pug.go
@@ -12,8 +12,7 @@ import (
// Pug (or Jade) returns a new pug view engine.
// It shares the same exactly logic with the
// html view engine, it uses the same exactly configuration.
-// It has got some features and a lot of functions
-// which will make your life easier.
+//
// Read more about the Jade Go Parser: https://github.com/Joker/jade
//
// Examples: