diff --git a/HISTORY.md b/HISTORY.md index f22a8e9c..11405bbd 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -19,6 +19,12 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene **How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris/v12@latest` and `go mod tidy -compat=1.20`. +# Next + +Change applies to `master` branch. + +- Remove [ace](https://github.com/eknkc/amber) template parser support, as it was discontinued by its author more than five years ago. + # Sa, 11 March 2023 | v12.2.0 This release introduces new features and some breaking changes. @@ -41,7 +47,7 @@ All new features have been tested in production and seem to work fine. Fixed all - Add `Context.Render` method for compatibility. - Support of embedded [locale files](https://github.com/kataras/iris/blob/master/_examples/i18n/template-embedded/main.go) using standard `embed.FS` with the new `LoadFS` function. -- Support of direct embedded view engines (`HTML, Blocks, Django, Handlebars, Pug, Amber, Jet` and `Ace`) with `embed.FS` or `fs.FS` (in addition to `string` and `http.FileSystem` types). +- Support of direct embedded view engines (`HTML, Blocks, Django, Handlebars, Pug, Jet` and `Ace`) with `embed.FS` or `fs.FS` (in addition to `string` and `http.FileSystem` types). - Add support for `embed.FS` and `fs.FS` on `app.HandleDir`. - Add `iris.Patches()` package-level function to customize Iris Request Context REST (and more to come) behavior. @@ -306,7 +312,7 @@ func main() { - Fix Response Recorder `Clone` concurrent access afterwards. - Add a `ParseTemplate` method on view engines to manually parse and add a template from a text as [requested](https://github.com/kataras/iris/issues/1617). [Examples](https://github.com/kataras/iris/tree/master/_examples/view/parse-template). -- Full `http.FileSystem` interface support for all **view** engines as [requested](https://github.com/kataras/iris/issues/1575). The first argument of the functions(`HTML`, `Blocks`, `Pug`, `Amber`, `Ace`, `Jet`, `Django`, `Handlebars`) can now be either a directory of `string` type (like before) or a value which completes the `http.FileSystem` interface. The `.Binary` method of all view engines was removed: pass the go-bindata's latest version `AssetFile()` exported function as the first argument instead of string. +- Full `http.FileSystem` interface support for all **view** engines as [requested](https://github.com/kataras/iris/issues/1575). The first argument of the functions(`HTML`, `Blocks`, `Pug`, `Ace`, `Jet`, `Django`, `Handlebars`) can now be either a directory of `string` type (like before) or a value which completes the `http.FileSystem` interface. The `.Binary` method of all view engines was removed: pass the go-bindata's latest version `AssetFile()` exported function as the first argument instead of string. - Add `Route.ExcludeSitemap() *Route` to exclude a route from sitemap as requested in [chat](https://chat.iris-go.com), also offline routes are excluded automatically now. diff --git a/README.md b/README.md index 4c4c96f4..dde82ada 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ Some of the features Iris offers: * Builtin support for ngrok to put your app on the internet, the fastest way * Unique Router with dynamic path as parameter with standard types like :uuid, :string, :int... and the ability to create your own * Compression -* View Engines (HTML, Django, Amber, Handlebars, Pug/Jade and more) +* View Engines (HTML, Django, Handlebars, Pug/Jade and more) * Create your own File Server and host your own WebDAV server * Cache * Localization (i18n, sitemap) diff --git a/README_PT_BR.md b/README_PT_BR.md index de1d3b40..df94d769 100644 --- a/README_PT_BR.md +++ b/README_PT_BR.md @@ -184,7 +184,7 @@ Alguns dos recursos que o Iris Web Framework oferece: * Suporte integrado para ngrok para colocar seu aplicativo na internet da maneira mais rápida * Router único com caminho dinâmico como parametro com tipos padrões como :uuid, :string, :int... e a habilidade de criar o seu próprio router * Compressão -* View Engines (HTML, Django, Amber, Handlebars, Pug/Jade e mais) +* View Engines (HTML, Django, Handlebars, Pug/Jade e mais) * Cria seu próprio Servidor de Arquivo e hospeda seu próprio servidor WebDAV * Cache * Localização (i18n, sitemap) diff --git a/README_ZH_HANT.md b/README_ZH_HANT.md index 8fcbe8b6..c1e5eea7 100644 --- a/README_ZH_HANT.md +++ b/README_ZH_HANT.md @@ -195,7 +195,7 @@ Iris 提供了至少這些功能: - 內建 ngrok 支援,讓您可以把 app 以最快速的方式推上網際網路 - 包含動態路徑、具唯一性的路由,支援如 :uuid、:string、:int 等等的標準類型,並且可以自己建立 - 壓縮功能 -- 檢視 (View) 算繪引擎 (HTML、Django、Amber、Handlebars、Pug/Jade 等等) +- 檢視 (View) 算繪引擎 (HTML、Django、Handlebars、Pug/Jade 等等) - 建立自己的檔案伺服器,並寄存您自己的 WebDAV 伺服器 - 快取 - 本地化 (i18n、sitemap) diff --git a/_benchmarks/view/README.md b/_benchmarks/view/README.md index 90fc7cdb..209e4c6a 100644 --- a/_benchmarks/view/README.md +++ b/_benchmarks/view/README.md @@ -1,26 +1,27 @@ # View Engine Benchmarks -Benchmark between all 8 supported template parsers. +Benchmark between all 7 supported template parsers. -Amber, Ace and Pug parsers minifies the template before render. So, to have a fair benchmark, we must make sure that the byte amount of the total response body is exactly the same across all. Therefore, all other template files are minified too. +Ace and Pug parsers minifies the template before render. So, to have a fair benchmark, we must make sure that the byte amount of the total response body is exactly the same across all. Therefore, all other template files are minified too. ![Benchmarks Chart Graph](chart.png) -> Last updated: Oct 1, 2020 at 12:46pm (UTC) +> Last updated: Mar 17, 2023 at 10:31am (UTC) ## System | | | |----|:---| -| Processor | Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz | -| RAM | 15.85 GB | -| OS | Microsoft Windows 10 Pro | +| Processor | 12th Gen Intel(R) Core(TM) i7-12700H | +| RAM | 15.68 GB | +| OS | Microsoft Windows 11 Pro | | [Bombardier](https://github.com/codesenberg/bombardier) | v1.2.4 | -| [Go](https://golang.org) | go1.15.2 | +| [Go](https://golang.org) | go1.20.2 | +| [Node.js](https://nodejs.org/) | v19.5.0 | ## Terminology -**Name** is the name of the template engine used under a particular test. +**Name** is the name of the framework(or router) used under a particular test. **Reqs/sec** is the avg number of total requests could be processed per second (the higher the better). @@ -38,19 +39,18 @@ Amber, Ace and Pug parsers minifies the template before render. So, to have a fa | Name | Language | Reqs/sec | Latency | Throughput | Time To Complete | |------|:---------|:---------|:--------|:-----------|:-----------------| -| [Amber](./amber) | Go |125698 |0.99ms |44.67MB |7.96s | -| [Blocks](./blocks) | Go |123974 |1.01ms |43.99MB |8.07s | -| [Django](./django) | Go |118831 |1.05ms |42.17MB |8.41s | -| [Handlebars](./handlebars) | Go |101214 |1.23ms |35.91MB |9.88s | -| [Pug](./pug) | Go |89002 |1.40ms |31.81MB |11.24s | -| [Ace](./ace) | Go |64782 |1.93ms |22.98MB |15.44s | -| [HTML](./html) | Go |53918 |2.32ms |19.13MB |18.55s | -| [Jet](./jet) | Go |4829 |25.88ms |1.71MB |207.07s | +| [Jet](./jet) | Go |248957 |500.81us |88.26MB |4.02s | +| [Blocks](./blocks) | Go |238854 |521.76us |84.74MB |4.19s | +| [Pug](./pug) | Go |238153 |523.74us |85.07MB |4.20s | +| [Django](./django) | Go |224448 |555.40us |79.61MB |4.46s | +| [Handlebars](./handlebars) | Go |197267 |631.99us |69.96MB |5.07s | +| [Ace](./ace) | Go |157415 |792.53us |55.83MB |6.35s | +| [HTML](./html) | Go |120811 |1.03ms |42.82MB |8.29s | ## How to Run ```sh -$ go install github.com/kataras/server-benchmarks@latest -$ go install github.com/codesenberg/bombardier@latest +$ go install github.com/kataras/server-benchmarks@master +$ go install github.com/codesenberg/bombardier@master $ server-benchmarks --wait-run=3s -o ./results ``` diff --git a/_benchmarks/view/ace/views/layouts/main.ace b/_benchmarks/view/ace/views/layouts/main.ace index b1e74baa..9453acad 100644 --- a/_benchmarks/view/ace/views/layouts/main.ace +++ b/_benchmarks/view/ace/views/layouts/main.ace @@ -3,6 +3,6 @@ html head title {{.Title}} body - {{ yield . . }} + {{ yield . }} footer = include partials/footer.ace . diff --git a/_benchmarks/view/amber/main.go b/_benchmarks/view/amber/main.go deleted file mode 100644 index 4b63f684..00000000 --- a/_benchmarks/view/amber/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - // By default Amber engine minifies the template before render. - app.RegisterView(iris.Amber("./views", ".amber")) - - app.Get("/", index) - - app.Listen(":8080") -} - -func index(ctx iris.Context) { - data := iris.Map{ - "Title": "Page Title", - "FooterText": "Footer contents", - "Message": "Main contents", - } - - // On Amber this is ignored: ctx.ViewLayout("layouts/main") - // Layouts are only rendered from inside the index page itself - // using the "extends" keyword. - if err := ctx.View("index", data); err != nil { - ctx.HTML("

%s

", err.Error()) - return - } -} diff --git a/_benchmarks/view/amber/views/index.amber b/_benchmarks/view/amber/views/index.amber deleted file mode 100644 index e92604dc..00000000 --- a/_benchmarks/view/amber/views/index.amber +++ /dev/null @@ -1,5 +0,0 @@ -extends layouts/main.amber - -block content - h1 Index Body - h3 Message: #{Message} \ No newline at end of file diff --git a/_benchmarks/view/amber/views/layouts/main.amber b/_benchmarks/view/amber/views/layouts/main.amber deleted file mode 100644 index b0b751a8..00000000 --- a/_benchmarks/view/amber/views/layouts/main.amber +++ /dev/null @@ -1,8 +0,0 @@ -doctype html -html - head - title #{Title} - body - block content - footer - #{render("partials/footer.amber", $)} \ No newline at end of file diff --git a/_benchmarks/view/amber/views/partials/footer.amber b/_benchmarks/view/amber/views/partials/footer.amber deleted file mode 100644 index a202d2c0..00000000 --- a/_benchmarks/view/amber/views/partials/footer.amber +++ /dev/null @@ -1,2 +0,0 @@ -h3 Footer Partial -h4 #{FooterText} \ No newline at end of file diff --git a/_benchmarks/view/chart.png b/_benchmarks/view/chart.png index 0403fea8..6ad909c6 100644 Binary files a/_benchmarks/view/chart.png and b/_benchmarks/view/chart.png differ diff --git a/_benchmarks/view/tests.yml b/_benchmarks/view/tests.yml index 00ee224c..a50a03fc 100644 --- a/_benchmarks/view/tests.yml +++ b/_benchmarks/view/tests.yml @@ -11,8 +11,6 @@ Envs: - Name: Ace Dir: ./ace - - Name: Amber - Dir: ./amber - Name: Blocks Dir: ./blocks - Name: Django diff --git a/_examples/README.md b/_examples/README.md index 7291211e..1b316c66 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -138,7 +138,6 @@ * [Overview](view/overview/main.go) * [Layout](view/layout) * [Ace](view/layout/ace) - * [Amber](view/layout/amber) * [Blocks](view/layout/blocks) * [Django](view/layout/django) * [Handlebars](view/layout/handlebars) @@ -160,7 +159,6 @@ * Parse a Template from Text * [HTML, Pug and Ace](view/parse-parse/main.go) * [Django](view/parse-parse/django/main.go) - * [Amber](view/parse-parse/amber/main.go) * [Jet](view/parse-parse/jet/main.go) * [Handlebars](view/parse-parse/handlebars/main.go) * [Blocks](view/template_blocks_0) @@ -170,8 +168,6 @@ * [Pug Embedded`](view/template_pug_2_embedded) * [Ace](view/template_ace_0) * [Django](view/template_django_0) - * [Amber](view/template_amber_0) - * [Amber Embedded](view/template_amber_1_embedded) * [Jet](view/template_jet_0) * [Jet Embedded](view/template_jet_1_embedded) * [Jet 'urlpath' tmpl func](view/template_jet_2) diff --git a/_examples/README_ZH_HANT.md b/_examples/README_ZH_HANT.md index 5d8abbaa..13c852b1 100644 --- a/_examples/README_ZH_HANT.md +++ b/_examples/README_ZH_HANT.md @@ -136,7 +136,6 @@ - [概覽](view/overview/main.go) - [排版引擎](view/layout) - [Ace](view/layout/ace) - - [Amber](view/layout/amber) - [Blocks](view/layout/blocks) - [Django](view/layout/django) - [Handlebars](view/layout/handlebars) @@ -158,7 +157,6 @@ - 從文字解析樣板 - [HTML, Pug and Ace](view/parse-parse/main.go) - [Django](view/parse-parse/django/main.go) - - [Amber](view/parse-parse/amber/main.go) - [Jet](view/parse-parse/jet/main.go) - [Handlebars](view/parse-parse/handlebars/main.go) - [Blocks](view/template_blocks_0) @@ -168,8 +166,6 @@ - [Pug Embedded`](view/template_pug_2_embedded) - [Ace](view/template_ace_0) - [Django](view/template_django_0) - - [Amber](view/template_amber_0) - - [Amber Embedded](view/template_amber_1_embedded) - [Jet](view/template_jet_0) - [Jet Embedded](view/template_jet_1_embedded) - [Jet 'urlpath' tmpl func](view/template_jet_2) diff --git a/_examples/view/context-view-data/templates/layouts/layout.html b/_examples/view/context-view-data/templates/layouts/layout.html index 4e0179c7..ef74f39c 100644 --- a/_examples/view/context-view-data/templates/layouts/layout.html +++ b/_examples/view/context-view-data/templates/layouts/layout.html @@ -5,6 +5,6 @@ - {{ yield . . }} + {{ yield . }} diff --git a/_examples/view/layout/amber/main.go b/_examples/view/layout/amber/main.go deleted file mode 100644 index 92195ee3..00000000 --- a/_examples/view/layout/amber/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - app.RegisterView(iris.Amber("./views", ".amber")) - - app.Get("/", index) - - app.Listen(":8080") -} - -func index(ctx iris.Context) { - data := iris.Map{ - "Title": "Page Title", - "FooterText": "Footer contents", - "Message": "Main contents", - } - - // On Amber this is ignored: ctx.ViewLayout("layouts/main") - // Layouts are only rendered from inside the index page itself - // using the "extends" keyword. - if err := ctx.View("index", data); err != nil { - ctx.HTML("

%s

", err.Error()) - return - } -} diff --git a/_examples/view/layout/amber/views/index.amber b/_examples/view/layout/amber/views/index.amber deleted file mode 100644 index e92604dc..00000000 --- a/_examples/view/layout/amber/views/index.amber +++ /dev/null @@ -1,5 +0,0 @@ -extends layouts/main.amber - -block content - h1 Index Body - h3 Message: #{Message} \ No newline at end of file diff --git a/_examples/view/layout/amber/views/layouts/main.amber b/_examples/view/layout/amber/views/layouts/main.amber deleted file mode 100644 index 2dbc577a..00000000 --- a/_examples/view/layout/amber/views/layouts/main.amber +++ /dev/null @@ -1,8 +0,0 @@ -doctype html -html - head - title #{Title} - body - block content - footer - #{render("partials/footer.amber", $)} \ No newline at end of file diff --git a/_examples/view/layout/amber/views/partials/footer.amber b/_examples/view/layout/amber/views/partials/footer.amber deleted file mode 100644 index a202d2c0..00000000 --- a/_examples/view/layout/amber/views/partials/footer.amber +++ /dev/null @@ -1,2 +0,0 @@ -h3 Footer Partial -h4 #{FooterText} \ No newline at end of file diff --git a/_examples/view/parse-template/amber/main.go b/_examples/view/parse-template/amber/main.go deleted file mode 100644 index 3fa5829d..00000000 --- a/_examples/view/parse-template/amber/main.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - e := iris.Amber(nil, ".amber") // You can still use a file system though. - e.AddFunc("greet", func(name string) string { - return "Hello, " + name + "!" - }) - err := e.ParseTemplate("program.amber", []byte(`h1 #{ greet(Name) }`)) - if err != nil { - panic(err) - } - e.Reload(true) - - app := iris.New() - app.RegisterView(e) - app.Get("/", index) - - app.Listen(":8080") -} - -func index(ctx iris.Context) { - if err := ctx.View("program.amber", iris.Map{ - "Name": "Gerasimos", - // Or per template: - // "greet": func(....) - }); err != nil { - ctx.HTML("

%s

", err.Error()) - return - } -} diff --git a/_examples/view/template_ace_0/views/index.ace b/_examples/view/template_ace_0/views/index.ace index 776cc11f..144910f0 100644 --- a/_examples/view/template_ace_0/views/index.ace +++ b/_examples/view/template_ace_0/views/index.ace @@ -2,4 +2,6 @@ h2 {{.Title}} +h3 Body + = include partials/footer.ace . \ No newline at end of file diff --git a/_examples/view/template_amber_0/main.go b/_examples/view/template_amber_0/main.go deleted file mode 100644 index 4d6155c0..00000000 --- a/_examples/view/template_amber_0/main.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - - // Read about its markup syntax at: https://github.com/eknkc/amber - tmpl := iris.Amber("./views", ".amber") - - app.RegisterView(tmpl) - - app.Get("/", func(ctx iris.Context) { - if err := ctx.View("index.amber", iris.Map{ - "Title": "Title of The Page", - }); err != nil { - ctx.HTML("

%s

", err.Error()) - return - } - }) - - app.Listen(":8080") -} diff --git a/_examples/view/template_amber_0/views/index.amber b/_examples/view/template_amber_0/views/index.amber deleted file mode 100644 index ab89a92a..00000000 --- a/_examples/view/template_amber_0/views/index.amber +++ /dev/null @@ -1,11 +0,0 @@ -extends layouts/main.amber - -block append meta - meta[name="keywords"][content="These are added by the child template"] - -block menu - li Item 1 - li Item 2 - -block content - p Content from child template \ No newline at end of file diff --git a/_examples/view/template_amber_0/views/layouts/main.amber b/_examples/view/template_amber_0/views/layouts/main.amber deleted file mode 100644 index b2f9ad6c..00000000 --- a/_examples/view/template_amber_0/views/layouts/main.amber +++ /dev/null @@ -1,15 +0,0 @@ -!!! transitional -html - head - title #{Title} - - block meta - meta[name="description"][value="This is a sample"] - - body - header#mainHeader - ul - block menu - - div#content - block content \ No newline at end of file diff --git a/_examples/view/template_amber_1_embedded/bindata.go b/_examples/view/template_amber_1_embedded/bindata.go deleted file mode 100644 index d92c4889..00000000 --- a/_examples/view/template_amber_1_embedded/bindata.go +++ /dev/null @@ -1,358 +0,0 @@ -// Code generated by go-bindata. (@generated) DO NOT EDIT. - -// Package main generated by go-bindata.// sources: -// ../template_amber_0/views/index.amber -// ../template_amber_0/views/layouts/main.amber -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -// Name return file name -func (fi bindataFileInfo) Name() string { - return fi.name -} - -// Size return file size -func (fi bindataFileInfo) Size() int64 { - return fi.size -} - -// Mode return file mode -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} - -// ModTime return file modify time -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} - -// IsDir return file whether a directory -func (fi bindataFileInfo) IsDir() bool { - return fi.mode&os.ModeDir != 0 -} - -// Sys return file is sys mode -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -type assetFile struct { - *bytes.Reader - name string - childInfos []os.FileInfo - childInfoOffset int -} - -type assetOperator struct{} - -// Open implement http.FileSystem interface -func (f *assetOperator) Open(name string) (http.File, error) { - var err error - if len(name) > 0 && name[0] == '/' { - name = name[1:] - } - content, err := Asset(name) - if err == nil { - return &assetFile{name: name, Reader: bytes.NewReader(content)}, nil - } - children, err := AssetDir(name) - if err == nil { - childInfos := make([]os.FileInfo, 0, len(children)) - for _, child := range children { - childPath := filepath.Join(name, child) - info, errInfo := AssetInfo(filepath.Join(name, child)) - if errInfo == nil { - childInfos = append(childInfos, info) - } else { - childInfos = append(childInfos, newDirFileInfo(childPath)) - } - } - return &assetFile{name: name, childInfos: childInfos}, nil - } else { - // If the error is not found, return an error that will - // result in a 404 error. Otherwise the server returns - // a 500 error for files not found. - if strings.Contains(err.Error(), "not found") { - return nil, os.ErrNotExist - } - return nil, err - } -} - -// Close no need do anything -func (f *assetFile) Close() error { - return nil -} - -// Readdir read dir's children file info -func (f *assetFile) Readdir(count int) ([]os.FileInfo, error) { - if len(f.childInfos) == 0 { - return nil, os.ErrNotExist - } - if count <= 0 { - return f.childInfos, nil - } - if f.childInfoOffset+count > len(f.childInfos) { - count = len(f.childInfos) - f.childInfoOffset - } - offset := f.childInfoOffset - f.childInfoOffset += count - return f.childInfos[offset : offset+count], nil -} - -// Stat read file info from asset item -func (f *assetFile) Stat() (os.FileInfo, error) { - if len(f.childInfos) != 0 { - return newDirFileInfo(f.name), nil - } - return AssetInfo(f.name) -} - -// newDirFileInfo return default dir file info -func newDirFileInfo(name string) os.FileInfo { - return &bindataFileInfo{ - name: name, - size: 0, - mode: os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir - modTime: time.Time{}} -} - -// AssetFile return a http.FileSystem instance that data backend by asset -func AssetFile() http.FileSystem { - return &assetOperator{} -} - -var _indexAmber = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x8d\x31\x8a\xc3\x30\x14\x44\x7b\x83\xef\x30\xe8\x00\xbb\xec\xf6\xae\xb6\xda\x3e\x9d\x71\xf1\x6d\x4d\xb0\xb1\xbe\x24\xa4\x6f\x12\xdf\x3e\x10\x07\x4c\xd2\xcd\x63\x86\x79\xbc\x1b\xa3\xaf\x08\xb2\xa7\xcd\xea\xb7\xca\x12\xbf\x44\x47\x96\xb6\x69\x9b\x31\xa4\x69\x85\xe4\xcc\xe8\xa1\x34\x69\x1b\x00\xcf\xd4\x47\x51\x76\x6e\xe5\x7e\x4b\xc5\x57\x37\xf4\x53\x8a\xc6\x68\x9d\xbb\xcc\xac\x84\x14\x42\xbc\xa7\xc7\xb8\xc3\x66\x62\x9a\x97\xe0\x61\xd4\x1c\xc4\xe8\x86\x53\xa0\x8c\xdb\xf1\x1c\x16\xfc\x1b\x15\x3f\xef\xf8\x7b\x6e\x5f\x96\xa3\xcf\xf8\x3b\x10\xd7\x92\xf4\xc3\xf0\x08\x00\x00\xff\xff\xaa\xd2\x87\xd5\xdb\x00\x00\x00") - -func indexAmberBytes() ([]byte, error) { - return bindataRead( - _indexAmber, - "index.amber", - ) -} - -func indexAmber() (*asset, error) { - bytes, err := indexAmberBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "index.amber", size: 219, mode: os.FileMode(438), modTime: time.Unix(1599264617, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _layoutsMainAmber = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x4e\xc1\xae\x83\x30\x0c\xbb\x23\xf1\x0f\xa1\x7c\x07\xf7\xf7\x01\xdc\x10\x87\x40\x23\x11\xbd\x34\x45\x34\x20\x4d\xd3\xfe\x7d\xea\xd8\x04\xf5\xa5\xae\xdd\xda\x6e\x9a\x06\x6c\x43\x4d\x6c\x1c\x15\xa5\xae\x16\x0b\x52\x57\x00\x00\x0b\xa1\x3f\x59\x86\xb1\x09\x41\xfb\xec\xf3\xf9\xaa\xab\xcb\x99\x24\xce\xff\x10\xc8\xf0\xd2\x32\xb2\x32\x28\x06\xea\x9c\xa7\x34\x6f\xbc\xe6\x0e\x37\x0e\x07\xca\x4e\x9d\xeb\x17\x4e\xc0\x09\x10\x12\x86\x55\xc8\x8d\xbf\xd4\x29\xfa\xc7\x95\x95\x77\xd0\xd6\x06\x64\xfd\xfb\xd0\xb2\x66\x97\xf2\x7e\x9f\xa4\xfb\x7d\xa8\xe7\xa3\x9d\xa3\x1a\xa9\x95\x5f\xce\xe7\x5f\xeb\x1d\x00\x00\xff\xff\x56\x99\x59\x53\x13\x01\x00\x00") - -func layoutsMainAmberBytes() ([]byte, error) { - return bindataRead( - _layoutsMainAmber, - "layouts/main.amber", - ) -} - -func layoutsMainAmber() (*asset, error) { - bytes, err := layoutsMainAmberBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "layouts/main.amber", size: 275, mode: os.FileMode(438), modTime: time.Unix(1599263735, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "index.amber": indexAmber, - "layouts/main.amber": layoutsMainAmber, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// -// data/ -// foo.txt -// img/ -// a.png -// b.png -// -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("nonexistent") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - canonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(canonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "index.amber": {indexAmber, map[string]*bintree{}}, - "layouts": {nil, map[string]*bintree{ - "main.amber": {layoutsMainAmber, map[string]*bintree{}}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = os.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - canonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) -} diff --git a/_examples/view/template_amber_1_embedded/main.go b/_examples/view/template_amber_1_embedded/main.go deleted file mode 100644 index 36b85315..00000000 --- a/_examples/view/template_amber_1_embedded/main.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -// $ go install github.com/go-bindata/go-bindata/v3/go-bindata@latest -// -// $ go-bindata -fs -prefix "../template_amber_0/views" ../template_amber_0/views/... -// $ go run . -// # OR: go-bindata -fs -prefix "views" ./views/... if the views dir is rel to the executable. -// -// System files are not used, you can optionally delete the folder and run the example now. - -func main() { - app := iris.New() - - // Read about its markup syntax at: https://github.com/eknkc/amber - tmpl := iris.Amber(AssetFile(), ".amber") - - app.RegisterView(tmpl) - - app.Get("/", func(ctx iris.Context) { - if err := ctx.View("index.amber", iris.Map{ - "Title": "Title of The Page", - }); err != nil { - ctx.HTML("

%s

", err.Error()) - return - } - }) - - app.Listen(":8080") -} diff --git a/_examples/view/template_jet_0/views/layouts/application.jet b/_examples/view/template_jet_0/views/layouts/application.jet index 9fce2365..d8945a34 100644 --- a/_examples/view/template_jet_0/views/layouts/application.jet +++ b/_examples/view/template_jet_0/views/layouts/application.jet @@ -2,7 +2,7 @@ - {{ isset(title) ? title : "" }} + {{ isset(title) ? title : "Default" }} {{block documentBody()}}{{end}} diff --git a/aliases.go b/aliases.go index 47d04e5c..f83fad7d 100644 --- a/aliases.go +++ b/aliases.go @@ -46,7 +46,7 @@ type ( // Developers get request information from the client's request by a Context. Context = *context.Context // ViewEngine is an alias of `context.ViewEngine`. - // See HTML, Blocks, Django, Jet, Pug, Ace, Handlebars, Amber and e.t.c. + // See HTML, Blocks, Django, Jet, Pug, Ace, Handlebars and e.t.c. ViewEngine = context.ViewEngine // UnmarshalerFunc a shortcut, an alias for the `context#UnmarshalerFunc` type // which implements the `context#Unmarshaler` interface for reading request's body @@ -267,9 +267,6 @@ var ( // Pug view engine. // Shortcut of the view.Pug. Pug = view.Pug - // Amber view engine. - // Shortcut of the view.Amber. - Amber = view.Amber // Jet view engine. // Shortcut of the view.Jet. Jet = view.Jet diff --git a/go.mod b/go.mod index 9b11fb85..b279310a 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/andybalholm/brotli v1.0.5 github.com/blang/semver/v4 v4.0.0 github.com/dgraph-io/badger/v2 v2.2007.4 - github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 github.com/fatih/structs v1.1.0 github.com/flosch/pongo2/v4 v4.0.2 github.com/golang/snappy v0.0.4 diff --git a/go.sum b/go.sum index aea4d4a7..a4e63bb6 100644 --- a/go.sum +++ b/go.sum @@ -49,8 +49,6 @@ github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= diff --git a/view/README.md b/view/README.md index a5232966..84914048 100644 --- a/view/README.md +++ b/view/README.md @@ -1,6 +1,6 @@ # View -Iris supports 8 template engines out-of-the-box, developers can still use any external golang template engine, +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 template engines share a common API i.e. @@ -13,9 +13,8 @@ Parse using embedded assets, Layouts and Party-specific layout, Template Funcs, | 3 | Django | [flosch/pongo2](https://github.com/flosch/pongo2) | | 4 | Pug | [Joker/jade](https://github.com/Joker/jade) | | 5 | Handlebars | [mailgun/raymond](https://github.com/mailgun/raymond) | -| 6 | Amber | [eknkc/amber](https://github.com/eknkc/amber) | -| 7 | Jet | [CloudyKit/jet](https://github.com/CloudyKit/jet) | -| 8 | Ace | [yosssi/ace](https://github.com/yosssi/ace) | +| 6 | Jet | [CloudyKit/jet](https://github.com/CloudyKit/jet) | +| 7 | Ace | [yosssi/ace](https://github.com/yosssi/ace) | [List of Examples](https://github.com/kataras/iris/tree/master/_examples/view). diff --git a/view/ace.go b/view/ace.go index 04fe746f..5da5e263 100644 --- a/view/ace.go +++ b/view/ace.go @@ -1,6 +1,7 @@ package view import ( + "strings" "sync" "github.com/yosssi/ace" @@ -62,6 +63,12 @@ func Ace(fs interface{}, extension string) *AceEngine { []*ace.File{}, ) + if strings.Contains(name, "layout") { + for k, v := range s.layoutFuncs { + funcs[k] = v + } + } + opts := &ace.Options{ Extension: extension[1:], FuncMap: funcs, diff --git a/view/amber.go b/view/amber.go deleted file mode 100644 index 72c6d90b..00000000 --- a/view/amber.go +++ /dev/null @@ -1,281 +0,0 @@ -package view - -import ( - "bytes" - "fmt" - "html/template" - "io" - "io/fs" - "net/http" - "os" - "path/filepath" - "strings" - "sync" - "sync/atomic" - - "github.com/kataras/iris/v12/context" - - "github.com/eknkc/amber" -) - -// AmberEngine contains the amber view engine structure. -type AmberEngine struct { - fs fs.FS - // files configuration - rootDir string - extension string - reload bool - // - rmu sync.RWMutex // locks for `ExecuteWiter` when `reload` is true. - templateCache map[string]*template.Template - bufPool *sync.Pool - - Options amber.Options -} - -var ( - _ Engine = (*AmberEngine)(nil) - _ EngineFuncer = (*AmberEngine)(nil) -) - -var amberOnce = new(uint32) - -// Amber creates and returns a new amber view engine. -// The given "extension" MUST begin with a dot. -// -// Usage: -// Amber("./views", ".amber") or -// Amber(iris.Dir("./views"), ".amber") or -// Amber(embed.FS, ".amber") or Amber(AssetFile(), ".amber") for embedded data. -func Amber(dirOrFS interface{}, extension string) *AmberEngine { - if atomic.LoadUint32(amberOnce) > 0 { - panic("Amber: cannot be registered twice as its internal implementation share the same template functions across instances.") - } else { - atomic.StoreUint32(amberOnce, 1) - } - - fileSystem := getFS(dirOrFS) - s := &AmberEngine{ - fs: fileSystem, - rootDir: "/", - extension: extension, - templateCache: make(map[string]*template.Template), - Options: amber.Options{ - PrettyPrint: false, - LineNumbers: false, - VirtualFilesystem: http.FS(fileSystem), - }, - bufPool: &sync.Pool{New: func() interface{} { - return new(bytes.Buffer) - }}, - } - - builtinFuncs := template.FuncMap{ - "render": func(name string, binding interface{}) (template.HTML, error) { - result, err := s.executeTemplateBuf(name, binding) - return template.HTML(result), err - }, - } - - for k, v := range builtinFuncs { - amber.FuncMap[k] = v - } - - return s -} - -// RootDir sets the directory to be used as a starting point -// to load templates from the provided file system. -func (s *AmberEngine) RootDir(root string) *AmberEngine { - if s.fs != nil && root != "" && root != "/" && root != "." && root != s.rootDir { - sub, err := fs.Sub(s.fs, s.rootDir) - if err != nil { - panic(err) - } - - s.fs = sub // here so the "middleware" can work. - } - - s.rootDir = filepath.ToSlash(root) - return s -} - -// Name returns the amber engine's name. -func (s *AmberEngine) Name() string { - return "Amber" -} - -// Ext returns the file extension which this view engine is responsible to render. -// If the filename extension on ExecuteWriter is empty then this is appended. -func (s *AmberEngine) Ext() string { - return s.extension -} - -// Reload if set to true the templates are reloading on each render, -// use it when you're in development and you're boring of restarting -// the whole app when you edit a template file. -// -// Note that if `true` is passed then only one `View -> ExecuteWriter` will be render each time, -// no concurrent access across clients, use it only on development status. -// It's good to be used side by side with the https://github.com/kataras/rizla reloader for go source files. -func (s *AmberEngine) Reload(developmentMode bool) *AmberEngine { - s.reload = developmentMode - return s -} - -// SetPrettyPrint if pretty printing is enabled. -// Pretty printing ensures that the output html is properly indented and in human readable form. -// Defaults to false, response is minified. -func (s *AmberEngine) SetPrettyPrint(pretty bool) *AmberEngine { - s.Options.PrettyPrint = true - return s -} - -// AddFunc adds the function to the template's function map. -// It is legal to overwrite elements of the default actions: -// - url func(routeName string, args ...string) string -// - urlpath func(routeName string, args ...string) string -// - render func(fullPartialName string) (template.HTML, error). -// -// Note that, Amber does not support functions per template, -// instead it's using the "call" directive so any template-specific -// functions should be passed using `Context.View/ViewData` binding data. -// This method will modify the global amber's FuncMap which considers -// as the "builtin" as this is the only way to actually add a function. -// Note that, if you use more than one amber engine, the functions are shared. -func (s *AmberEngine) AddFunc(funcName string, funcBody interface{}) { - s.rmu.Lock() - amber.FuncMap[funcName] = funcBody - s.rmu.Unlock() -} - -// Load parses the templates to the engine. -// It is responsible to add the necessary global functions. -// -// Returns an error if something bad happens, user is responsible to catch it. -func (s *AmberEngine) Load() error { - // If only custom templates should be loaded. - if (s.fs == nil || context.IsNoOpFS(s.fs)) && len(s.templateCache) > 0 { - return nil - } - - rootDirName := getRootDirName(s.fs) - - return walk(s.fs, "", func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if info == nil || info.IsDir() { - return nil - } - - if s.extension != "" { - if !strings.HasSuffix(path, s.extension) { - return nil - } - } - - if s.rootDir == rootDirName { - path = strings.TrimPrefix(path, rootDirName) - path = strings.TrimPrefix(path, "/") - } - - contents, err := asset(s.fs, path) - if err != nil { - return fmt.Errorf("%s: %w", path, err) - } - - err = s.ParseTemplate(path, contents) - if err != nil { - return fmt.Errorf("%s: %v", path, err) - } - return nil - }) -} - -// ParseTemplate adds a custom template from text. -// This template parser does not support funcs per template directly. -// Two ways to add a function: -// Globally: Use `AddFunc` or pass them on `View` instead. -// Per Template: Use `Context.ViewData/View`. -func (s *AmberEngine) ParseTemplate(name string, contents []byte) error { - s.rmu.Lock() - defer s.rmu.Unlock() - - comp := amber.New() - comp.Options = s.Options - - err := comp.ParseData(contents, name) - if err != nil { - return err - } - - data, err := comp.CompileString() - if err != nil { - return err - } - - name = strings.TrimPrefix(name, "/") - - /* - New(...).Funcs(s.builtinFuncs): - This won't work on amber, it loads only amber.FuncMap which is global. - Relative code: - https://github.com/eknkc/amber/blob/cdade1c073850f4ffc70a829e31235ea6892853b/compiler.go#L771-L794 - */ - - tmpl := template.New(name).Funcs(amber.FuncMap) - _, err = tmpl.Parse(data) - if err == nil { - s.templateCache[name] = tmpl - } - - return err -} - -func (s *AmberEngine) executeTemplateBuf(name string, binding interface{}) (string, error) { - buf := s.bufPool.Get().(*bytes.Buffer) - buf.Reset() - - tmpl := s.fromCache(name) - if tmpl == nil { - s.bufPool.Put(buf) - return "", ErrNotExist{Name: name, IsLayout: false, Data: binding} - } - - err := tmpl.ExecuteTemplate(buf, name, binding) - result := strings.TrimSuffix(buf.String(), "\n") // on amber it adds a new line. - s.bufPool.Put(buf) - return result, err -} - -func (s *AmberEngine) fromCache(relativeName string) *template.Template { - if s.reload { - s.rmu.RLock() - defer s.rmu.RUnlock() - } - - if tmpl, ok := s.templateCache[relativeName]; ok { - return tmpl - } - - return nil -} - -// ExecuteWriter executes a template and writes its result to the w writer. -// layout here is useless. -func (s *AmberEngine) ExecuteWriter(w io.Writer, filename string, layout string, bindingData interface{}) error { - // re-parse the templates if reload is enabled. - if s.reload { - if err := s.Load(); err != nil { - return err - } - } - - if tmpl := s.fromCache(filename); tmpl != nil { - return tmpl.Execute(w, bindingData) - } - - return ErrNotExist{Name: filename, IsLayout: false, Data: bindingData} -}