mirror of
https://github.com/kataras/iris.git
synced 2025-01-22 18:21:03 +01:00
update view benchmarks and remove ace template parser
This commit is contained in:
parent
bd1a1486f4
commit
ec2d9d016e
10
HISTORY.md
10
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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -3,6 +3,6 @@ html
|
|||
head
|
||||
title {{.Title}}
|
||||
body
|
||||
{{ yield . . }}
|
||||
{{ yield . }}
|
||||
footer
|
||||
= include partials/footer.ace .
|
||||
|
|
|
@ -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("<h3>%s</h3>", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
extends layouts/main.amber
|
||||
|
||||
block content
|
||||
h1 Index Body
|
||||
h3 Message: #{Message}
|
|
@ -1,8 +0,0 @@
|
|||
doctype html
|
||||
html
|
||||
head
|
||||
title #{Title}
|
||||
body
|
||||
block content
|
||||
footer
|
||||
#{render("partials/footer.amber", $)}
|
|
@ -1,2 +0,0 @@
|
|||
h3 Footer Partial
|
||||
h4 #{FooterText}
|
Binary file not shown.
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 16 KiB |
|
@ -11,8 +11,6 @@
|
|||
Envs:
|
||||
- Name: Ace
|
||||
Dir: ./ace
|
||||
- Name: Amber
|
||||
Dir: ./amber
|
||||
- Name: Blocks
|
||||
Dir: ./blocks
|
||||
- Name: Django
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
</head>
|
||||
<body>
|
||||
<!-- Render the current template here -->
|
||||
{{ yield . . }}
|
||||
{{ yield . }}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -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("<h3>%s</h3>", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
extends layouts/main.amber
|
||||
|
||||
block content
|
||||
h1 Index Body
|
||||
h3 Message: #{Message}
|
|
@ -1,8 +0,0 @@
|
|||
doctype html
|
||||
html
|
||||
head
|
||||
title #{Title}
|
||||
body
|
||||
block content
|
||||
footer
|
||||
#{render("partials/footer.amber", $)}
|
|
@ -1,2 +0,0 @@
|
|||
h3 Footer Partial
|
||||
h4 #{FooterText}
|
|
@ -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("<h3>%s</h3>", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
|
@ -2,4 +2,6 @@
|
|||
|
||||
h2 {{.Title}}
|
||||
|
||||
h3 Body
|
||||
|
||||
= include partials/footer.ace .
|
|
@ -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("<h3>%s</h3>", err.Error())
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
app.Listen(":8080")
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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, "/")...)...)
|
||||
}
|
|
@ -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("<h3>%s</h3>", err.Error())
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
app.Listen(":8080")
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ isset(title) ? title : "" }}</title>
|
||||
<title>{{ isset(title) ? title : "Default" }}</title>
|
||||
</head>
|
||||
<body>
|
||||
{{block documentBody()}}{{end}}
|
||||
|
|
|
@ -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
|
||||
|
|
1
go.mod
1
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
|
||||
|
|
2
go.sum
generated
2
go.sum
generated
|
@ -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=
|
||||
|
|
|
@ -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).
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
281
view/amber.go
281
view/amber.go
|
@ -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}
|
||||
}
|
Loading…
Reference in New Issue
Block a user