Full support of the http.FileSystem on all view engines as requested at #1575

Also, the HandleDir accepts both string and http.FileSystem (interface{}) (like the view's fs)
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-09-05 08:34:09 +03:00
parent 7e6d453dad
commit e1f25eb098
No known key found for this signature in database
GPG Key ID: 5DBE766BD26A54E7
50 changed files with 1613 additions and 1226 deletions

View File

@ -364,6 +364,8 @@ Response:
Other Improvements: Other Improvements:
- 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.
- 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. - 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.
- Improved tracing (with `app.Logger().SetLevel("debug")`) for routes. Screens: - Improved tracing (with `app.Logger().SetLevel("debug")`) for routes. Screens:
@ -641,6 +643,7 @@ New Context Methods:
Breaking Changes: Breaking Changes:
- 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. All examples updated.
- `ContextUploadFormFiles(destDirectory string, before ...func(*Context, *multipart.FileHeader) bool) (uploaded []*multipart.FileHeader, n int64, err error)` now returns the total files uploaded too (as its first parameter) and the "before" variadic option should return a boolean, if false then the specific file is skipped. - `ContextUploadFormFiles(destDirectory string, before ...func(*Context, *multipart.FileHeader) bool) (uploaded []*multipart.FileHeader, n int64, err error)` now returns the total files uploaded too (as its first parameter) and the "before" variadic option should return a boolean, if false then the specific file is skipped.
- `Context.PostValues(name string) ([]string, error)` now returns a second output argument of `error` type too, which reports `ErrEmptyForm` or `ErrNotFound` or `ErrEmptyFormField`. The single post value getters now returns the **last value** if multiple was given instead of the first one (this allows clients to append values on flow updates). - `Context.PostValues(name string) ([]string, error)` now returns a second output argument of `error` type too, which reports `ErrEmptyForm` or `ErrNotFound` or `ErrEmptyFormField`. The single post value getters now returns the **last value** if multiple was given instead of the first one (this allows clients to append values on flow updates).
- `Party.GetReporter()` **removed**. The `Application.Build` returns the first error now and the API's errors are logged, this allows the server to run even if some of the routes are invalid but not fatal to the entire application (it was a request from a company). - `Party.GetReporter()` **removed**. The `Application.Build` returns the first error now and the API's errors are logged, this allows the server to run even if some of the routes are invalid but not fatal to the entire application (it was a request from a company).
@ -652,7 +655,7 @@ Breaking Changes:
- `iris.Gzip` and `iris.GzipReader` replaced with `iris.Compression` (middleware). - `iris.Gzip` and `iris.GzipReader` replaced with `iris.Compression` (middleware).
- `ctx.ClientSupportsGzip() bool` replaced with `ctx.ClientSupportsEncoding("gzip", "br" ...) bool`. - `ctx.ClientSupportsGzip() bool` replaced with `ctx.ClientSupportsEncoding("gzip", "br" ...) bool`.
- `ctx.GzipResponseWriter()` is **removed**. - `ctx.GzipResponseWriter()` is **removed**.
- `Party.HandleDir/iris.FileServer` now accepts a `http.FileSystem` instead of a string and returns a list of `[]*Route` (GET and HEAD) instead of GET only. Write: `app.HandleDir("/", iris.Dir("./assets"))` instead of `app.HandleDir("/", "./assets")` and `DirOptions.Asset, AssetNames, AssetInfo` removed, use `go-bindata -fs [..]` and `app.HandleDir("/", AssetFile())` instead. - `Party.HandleDir/iris.FileServer` now accepts both `http.FileSystem` and `string` and returns a list of `[]*Route` (GET and HEAD) instead of GET only. You can write: both `app.HandleDir("/", iris.Dir("./assets"))` and `app.HandleDir("/", "./assets")` and `DirOptions.Asset, AssetNames, AssetInfo` removed, use `go-bindata -fs [..]` and `app.HandleDir("/", AssetFile())` instead.
- `Context.OnClose` and `Context.OnCloseConnection` now both accept an `iris.Handler` instead of a simple `func()` as their callback. - `Context.OnClose` and `Context.OnCloseConnection` now both accept an `iris.Handler` instead of a simple `func()` as their callback.
- `Context.StreamWriter(writer func(w io.Writer) bool)` changed to `StreamWriter(writer func(w io.Writer) error) error` and it's now the `Context.Request().Context().Done()` channel that is used to receive any close connection/manual cancel signals, instead of the deprecated `ResponseWriter().CloseNotify()` one. Same for the `Context.OnClose` and `Context.OnCloseConnection` methods. - `Context.StreamWriter(writer func(w io.Writer) bool)` changed to `StreamWriter(writer func(w io.Writer) error) error` and it's now the `Context.Request().Context().Done()` channel that is used to receive any close connection/manual cancel signals, instead of the deprecated `ResponseWriter().CloseNotify()` one. Same for the `Context.OnClose` and `Context.OnCloseConnection` methods.
- Fixed handler's error response not be respected when response recorder was used instead of the common writer. Fixes [#1531](https://github.com/kataras/iris/issues/1531). It contains a **BREAKING CHANGE** of: the new `Configuration.ResetOnFireErrorCode` field should be set **to true** in order to behave as it used before this update (to reset the contents on recorder). - Fixed handler's error response not be respected when response recorder was used instead of the common writer. Fixes [#1531](https://github.com/kataras/iris/issues/1531). It contains a **BREAKING CHANGE** of: the new `Configuration.ResetOnFireErrorCode` field should be set **to true** in order to behave as it used before this update (to reset the contents on recorder).

View File

@ -113,15 +113,16 @@
* [Write to a custom `io.Writer`](view/write-to) * [Write to a custom `io.Writer`](view/write-to)
* [Blocks](view/template_blocks_0) * [Blocks](view/template_blocks_0)
* [Blocks Embedded](view/template_blocks_1_embedded) * [Blocks Embedded](view/template_blocks_1_embedded)
* [Pug: Greeting](view/template_pug_0) * [Pug: `Actions`](view/template_pug_0)
* [Pug: `Actions`](view/template_pug_1) * [Pug: `Includes`](view/template_pug_1)
* [Pug: `Includes`](view/template_pug_2) * [Pug Embedded`](view/template_pug_2_embedded)
* [Pug: `Extends`](view/template_pug_3) * [Jet](view/template_jet_0)
* [Jet Template](view/template_jet_0)
* [Jet Embedded](view/template_jet_1_embedded) * [Jet Embedded](view/template_jet_1_embedded)
* [Jet 'urlpath' tmpl func](view/template_jet_2) * [Jet 'urlpath' tmpl func](view/template_jet_2)
* [Jet Template Funcs from Struct](view/template_jet_3) * [Jet Template Funcs from Struct](view/template_jet_3)
* [Ace](view/template_ace_0) * [Ace](view/template_ace_0)
* [Amber](view/template_amber_0)
* [Amber Embedded](view/template_amber_1_embedded)
* [Handlebars](view/template_handlebars_0) * [Handlebars](view/template_handlebars_0)
* Third-Parties * Third-Parties
* [Render `valyala/quicktemplate` templates](view/quicktemplate) * [Render `valyala/quicktemplate` templates](view/quicktemplate)

View File

@ -5,7 +5,9 @@ import (
) )
// Follow these steps first: // Follow these steps first:
// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata // $ go get -u github.com/go-bindata/go-bindata
// # OR: go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// # to save it to your go.mod file
// $ go-bindata -prefix "assets" -fs ./assets/... // $ go-bindata -prefix "assets" -fs ./assets/...
// $ go run . // $ go run .
// "physical" files are not used, you can delete the "assets" folder and run the example. // "physical" files are not used, you can delete the "assets" folder and run the example.

View File

@ -6,7 +6,9 @@ import (
// How to run: // How to run:
// //
// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata // $ go get -u github.com/go-bindata/go-bindata
// # OR: go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// # to save it to your go.mod file
// $ go-bindata -prefix "../embedding-files-into-app/assets/" -fs ../embedding-files-into-app/assets/... // $ go-bindata -prefix "../embedding-files-into-app/assets/" -fs ../embedding-files-into-app/assets/...
// $ go run . // $ go run .
// Time to complete the compression and caching of [2/3] files: 31.9998ms // Time to complete the compression and caching of [2/3] files: 31.9998ms

View File

@ -7,7 +7,9 @@ import (
) )
// How to run: // How to run:
// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata // $ go get -u github.com/go-bindata/go-bindata
// # OR: go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// # to save it to your go.mod file
// $ go-bindata -nomemcopy -fs -prefix "../http2push/assets" ../http2push/assets/... // $ go-bindata -nomemcopy -fs -prefix "../http2push/assets" ../http2push/assets/...
// # OR if the ./assets directory was inside this example foder: // # OR if the ./assets directory was inside this example foder:
// # go-bindata -nomemcopy -refix "assets" ./assets/... // # go-bindata -nomemcopy -refix "assets" ./assets/...

View File

@ -2,7 +2,9 @@ package main
import "github.com/kataras/iris/v12" import "github.com/kataras/iris/v12"
// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata // $ go get -u github.com/go-bindata/go-bindata
// # OR: go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// # to save it to your go.mod file
// $ go-bindata -fs -prefix "public" ./public/... // $ go-bindata -fs -prefix "public" ./public/...
// $ go run . // $ go run .

View File

@ -20,8 +20,8 @@ import (
"time" "time"
) )
func bindataRead(data, name string) ([]byte, error) { func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(strings.NewReader(data)) gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil { if err != nil {
return nil, fmt.Errorf("read %q: %v", name, err) return nil, fmt.Errorf("read %q: %v", name, err)
} }
@ -168,82 +168,82 @@ func AssetFile() http.FileSystem {
return &assetOperator{} return &assetOperator{}
} }
var _publicAppJs = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2a\xcf\xcc\x4b\xc9\x2f\xd7\x4b\xcc\x49\x2d\x2a\xd1\x50\x4a\x2c\x28\xd0\xcb\x2a\x56\xc8\xc9\x4f\x4c\x49\x4d\x51\x48\x2b\xca\xcf\x55\x88\x51\xd2\x57\xd2\xb4\x06\x04\x00\x00\xff\xff\xa9\x06\xf7\xa3\x27\x00\x00\x00" var _appJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2a\xcf\xcc\x4b\xc9\x2f\xd7\x4b\xcc\x49\x2d\x2a\xd1\x50\x4a\x2c\x28\xd0\xcb\x2a\x56\xc8\xc9\x4f\x4c\x49\x4d\x51\x48\x2b\xca\xcf\x55\x88\x51\xd2\x57\xd2\xb4\x06\x04\x00\x00\xff\xff\xa9\x06\xf7\xa3\x27\x00\x00\x00")
func publicAppJsBytes() ([]byte, error) { func appJsBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_publicAppJs, _appJs,
"public/app.js", "app.js",
) )
} }
func publicAppJs() (*asset, error) { func appJs() (*asset, error) {
bytes, err := publicAppJsBytes() bytes, err := appJsBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "public/app.js", size: 39, mode: os.FileMode(438), modTime: time.Unix(1595516291, 0)} info := bindataFileInfo{name: "app.js", size: 39, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _publicApp2IndexHtml = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\x8d\x41\x0a\xc2\x40\x0c\x45\xf7\x81\xdc\xe1\x9f\xc0\xd0\xae\x43\xc0\x9d\xd7\xa8\x4c\x24\x85\xd4\x09\x32\x0b\xbd\xbd\xb4\xd6\xe5\x87\xf7\xde\xd7\x18\x5b\x1a\x13\x93\x86\x2f\xcd\x98\x00\x40\xc7\x3a\xd2\xed\x5a\x85\x59\xe5\x37\x98\x54\x4e\x84\x49\xef\xbd\x7d\xfe\x70\x4c\x86\x9b\x67\x76\x3c\x5e\x7d\xc3\x52\x35\xcb\xfa\x6c\xfe\xbe\xec\x71\xa8\xc4\x74\xd8\xa7\x73\x84\xf6\xd7\x6f\x00\x00\x00\xff\xff\xfd\x28\x92\x95\x7c\x00\x00\x00" var _app2IndexHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\x8d\x41\x0a\xc2\x40\x0c\x45\xf7\x81\xdc\xe1\x9f\xc0\xd0\xae\x43\xc0\x9d\xd7\xa8\x4c\x24\x85\xd4\x09\x32\x0b\xbd\xbd\xb4\xd6\xe5\x87\xf7\xde\xd7\x18\x5b\x1a\x13\x93\x86\x2f\xcd\x98\x00\x40\xc7\x3a\xd2\xed\x5a\x85\x59\xe5\x37\x98\x54\x4e\x84\x49\xef\xbd\x7d\xfe\x70\x4c\x86\x9b\x67\x76\x3c\x5e\x7d\xc3\x52\x35\xcb\xfa\x6c\xfe\xbe\xec\x71\xa8\xc4\x74\xd8\xa7\x73\x84\xf6\xd7\x6f\x00\x00\x00\xff\xff\xfd\x28\x92\x95\x7c\x00\x00\x00")
func publicApp2IndexHtmlBytes() ([]byte, error) { func app2IndexHtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_publicApp2IndexHtml, _app2IndexHtml,
"public/app2/index.html", "app2/index.html",
) )
} }
func publicApp2IndexHtml() (*asset, error) { func app2IndexHtml() (*asset, error) {
bytes, err := publicApp2IndexHtmlBytes() bytes, err := app2IndexHtmlBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "public/app2/index.html", size: 124, mode: os.FileMode(438), modTime: time.Unix(1572105320, 0)} info := bindataFileInfo{name: "app2/index.html", size: 124, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _publicCssMainCss = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xca\x4f\xa9\x54\xa8\xe6\xe5\x52\x50\x50\x50\x48\x4a\x4c\xce\x4e\x2f\xca\x2f\xcd\x4b\xd1\x4d\xce\xcf\xc9\x2f\xb2\x52\x48\xca\x49\x4c\xce\xb6\xe6\xe5\xaa\xe5\xe5\x02\x04\x00\x00\xff\xff\x03\x25\x9c\x89\x29\x00\x00\x00" var _cssMainCss = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xca\x4f\xa9\x54\xa8\xe6\xe5\x52\x50\x50\x50\x48\x4a\x4c\xce\x4e\x2f\xca\x2f\xcd\x4b\xd1\x4d\xce\xcf\xc9\x2f\xb2\x52\x48\xca\x49\x4c\xce\xb6\xe6\xe5\xaa\xe5\xe5\x02\x04\x00\x00\xff\xff\x03\x25\x9c\x89\x29\x00\x00\x00")
func publicCssMainCssBytes() ([]byte, error) { func cssMainCssBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_publicCssMainCss, _cssMainCss,
"public/css/main.css", "css/main.css",
) )
} }
func publicCssMainCss() (*asset, error) { func cssMainCss() (*asset, error) {
bytes, err := publicCssMainCssBytes() bytes, err := cssMainCssBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "public/css/main.css", size: 41, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} info := bindataFileInfo{name: "css/main.css", size: 41, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _publicIndexHtml = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\x8e\x41\x0e\xc2\x20\x10\x45\xf7\x24\xdc\xe1\xa7\x07\x80\x74\x3f\xb2\x76\xe9\xc2\x0b\x60\x41\xc1\x50\x21\xc0\x42\xd3\xf4\xee\x06\x4a\x97\x93\xf7\x66\xde\x90\xab\x6b\x50\x9c\x71\x46\xce\x6a\xa3\x38\x03\x00\xaa\xbe\x06\xab\xb6\x0d\xe2\xa6\x5f\x56\xdc\xdb\x88\x7d\x27\x79\x00\xce\x48\x0e\x9d\x33\x7a\x44\xf3\x3b\x17\xdd\xac\x70\xb5\x21\x44\x3c\x73\x5c\xe1\x3f\xc6\x7e\x45\x6b\x80\xa4\x9b\xbb\x3f\xcc\xb2\x64\x9f\x2a\x4a\x5e\x2e\x93\xd4\x29\x89\x77\x99\x14\x40\xf2\x00\xbd\x31\x2e\xf7\x5c\xfb\xf3\x1f\x00\x00\xff\xff\x25\xe9\x37\x57\xae\x00\x00\x00" var _indexHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\x8e\x41\x0e\xc2\x20\x10\x45\xf7\x24\xdc\xe1\xa7\x07\x80\x74\x3f\xb2\x76\xe9\xc2\x0b\x60\x41\xc1\x50\x21\xc0\x42\xd3\xf4\xee\x06\x4a\x97\x93\xf7\x66\xde\x90\xab\x6b\x50\x9c\x71\x46\xce\x6a\xa3\x38\x03\x00\xaa\xbe\x06\xab\xb6\x0d\xe2\xa6\x5f\x56\xdc\xdb\x88\x7d\x27\x79\x00\xce\x48\x0e\x9d\x33\x7a\x44\xf3\x3b\x17\xdd\xac\x70\xb5\x21\x44\x3c\x73\x5c\xe1\x3f\xc6\x7e\x45\x6b\x80\xa4\x9b\xbb\x3f\xcc\xb2\x64\x9f\x2a\x4a\x5e\x2e\x93\xd4\x29\x89\x77\x99\x14\x40\xf2\x00\xbd\x31\x2e\xf7\x5c\xfb\xf3\x1f\x00\x00\xff\xff\x25\xe9\x37\x57\xae\x00\x00\x00")
func publicIndexHtmlBytes() ([]byte, error) { func indexHtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_publicIndexHtml, _indexHtml,
"public/index.html", "index.html",
) )
} }
func publicIndexHtml() (*asset, error) { func indexHtml() (*asset, error) {
bytes, err := publicIndexHtmlBytes() bytes, err := indexHtmlBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "public/index.html", size: 174, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} info := bindataFileInfo{name: "index.html", size: 174, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -300,10 +300,10 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name. // _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){ var _bindata = map[string]func() (*asset, error){
"public/app.js": publicAppJs, "app.js": appJs,
"public/app2/index.html": publicApp2IndexHtml, "app2/index.html": app2IndexHtml,
"public/css/main.css": publicCssMainCss, "css/main.css": cssMainCss,
"public/index.html": publicIndexHtml, "index.html": indexHtml,
} }
// AssetDir returns the file names below a certain // AssetDir returns the file names below a certain
@ -347,16 +347,14 @@ type bintree struct {
} }
var _bintree = &bintree{nil, map[string]*bintree{ var _bintree = &bintree{nil, map[string]*bintree{
"public": {nil, map[string]*bintree{ "app.js": {appJs, map[string]*bintree{}},
"app.js": {publicAppJs, map[string]*bintree{}}, "app2": {nil, map[string]*bintree{
"app2": {nil, map[string]*bintree{ "index.html": {app2IndexHtml, map[string]*bintree{}},
"index.html": {publicApp2IndexHtml, map[string]*bintree{}},
}},
"css": {nil, map[string]*bintree{
"main.css": {publicCssMainCss, map[string]*bintree{}},
}},
"index.html": {publicIndexHtml, map[string]*bintree{}},
}}, }},
"css": {nil, map[string]*bintree{
"main.css": {cssMainCss, map[string]*bintree{}},
}},
"index.html": {indexHtml, map[string]*bintree{}},
}} }}
// RestoreAsset restores an asset under the given directory // RestoreAsset restores an asset under the given directory

View File

@ -4,8 +4,10 @@ import (
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
) )
// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata // $ go get -u github.com/go-bindata/go-bindata
// $ go-bindata -nomemcopy -fs ./public/... // # OR: go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// # to save it to your go.mod file
// $ go-bindata -fs -prefix "public" ./public/...
// $ go run . // $ go run .
var page = struct { var page = struct {
@ -14,20 +16,16 @@ var page = struct {
func newApp() *iris.Application { func newApp() *iris.Application {
app := iris.New() app := iris.New()
app.RegisterView(iris.HTML("./public", ".html").Binary(Asset, AssetNames))
app.RegisterView(iris.HTML(AssetFile(), ".html"))
app.HandleDir("/", AssetFile())
app.Get("/", func(ctx iris.Context) { app.Get("/", func(ctx iris.Context) {
ctx.ViewData("Page", page) ctx.ViewData("Page", page)
ctx.View("index.html") ctx.View("index.html")
}) })
// We didn't add a `-prefix "public"` argument on go-bindata command
// because the view's `Assset` and `AssetNames` require fullpath.
// Make use of the `PrefixDir` to serve assets on cases like that;
// when bindata.go file contains files that are
// not necessary public assets to be served.
app.HandleDir("/", iris.PrefixDir("public", AssetFile()))
return app return app
} }

View File

@ -1,5 +1,6 @@
// Code generated for package main by go-bindata DO NOT EDIT. (@generated) // Code generated by go-bindata. (@generated) DO NOT EDIT.
// sources:
//Package main generated by go-bindata.// sources:
// templates/layouts/layout.html // templates/layouts/layout.html
// templates/layouts/mylayout.html // templates/layouts/mylayout.html
// templates/page1.html // templates/page1.html
@ -12,6 +13,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -21,7 +23,7 @@ import (
func bindataRead(data []byte, name string) ([]byte, error) { func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data)) gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil { if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err) return nil, fmt.Errorf("read %q: %v", name, err)
} }
var buf bytes.Buffer var buf bytes.Buffer
@ -29,7 +31,7 @@ func bindataRead(data []byte, name string) ([]byte, error) {
clErr := gz.Close() clErr := gz.Close()
if err != nil { if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err) return nil, fmt.Errorf("read %q: %v", name, err)
} }
if clErr != nil { if clErr != nil {
return nil, err return nil, err
@ -65,7 +67,7 @@ func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode return fi.mode
} }
// Mode return file modify time // ModTime return file modify time
func (fi bindataFileInfo) ModTime() time.Time { func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime return fi.modTime
} }
@ -80,82 +82,168 @@ func (fi bindataFileInfo) Sys() interface{} {
return nil return nil
} }
var _templatesLayoutsLayoutHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\xce\xc1\xa9\xc3\x30\x0c\x06\xe0\xf3\x33\x78\x07\xbd\x01\x8c\xc9\x5d\x78\x82\x9e\x4a\x17\x70\x6a\x51\x19\x94\xa4\x38\xca\xc1\x84\xec\x5e\xec\xba\x27\x49\xf0\x89\xff\x47\xd6\x45\x82\x35\xc8\x14\x53\x9b\x9a\x55\x28\xdc\x62\xdd\x0e\x45\xff\xbd\xac\xb1\x06\xfd\x4f\xcc\x5b\xaa\xc1\x9a\x3f\xe4\x29\x3c\x38\xef\x90\x77\x50\x26\x78\xc9\x36\x47\x01\x19\xaf\x3c\x75\x34\x17\xf0\x7d\xf9\x77\x0e\xee\xb4\x26\x2a\x5d\x3f\x8f\x52\x68\x55\x50\x5a\xde\x12\x95\x80\xa9\x10\x38\xd7\xec\x79\x42\xcd\x24\x09\xae\xab\x05\x8f\x40\xf4\xa3\xeb\x27\x00\x00\xff\xff\x68\xca\x16\xc2\xb4\x00\x00\x00") type assetFile struct {
*bytes.Reader
name string
childInfos []os.FileInfo
childInfoOffset int
}
func templatesLayoutsLayoutHtmlBytes() ([]byte, error) { 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 _layoutsLayoutHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\xce\xc1\xa9\xc3\x30\x0c\x06\xe0\xf3\x33\x78\x07\xbd\x01\x8c\xc9\x5d\x78\x82\x9e\x4a\x17\x70\x6a\x51\x19\x94\xa4\x38\xca\xc1\x84\xec\x5e\xec\xba\x27\x49\xf0\x89\xff\x47\xd6\x45\x82\x35\xc8\x14\x53\x9b\x9a\x55\x28\xdc\x62\xdd\x0e\x45\xff\xbd\xac\xb1\x06\xfd\x4f\xcc\x5b\xaa\xc1\x9a\x3f\xe4\x29\x3c\x38\xef\x90\x77\x50\x26\x78\xc9\x36\x47\x01\x19\xaf\x3c\x75\x34\x17\xf0\x7d\xf9\x77\x0e\xee\xb4\x26\x2a\x5d\x3f\x8f\x52\x68\x55\x50\x5a\xde\x12\x95\x80\xa9\x10\x38\xd7\xec\x79\x42\xcd\x24\x09\xae\xab\x05\x8f\x40\xf4\xa3\xeb\x27\x00\x00\xff\xff\x68\xca\x16\xc2\xb4\x00\x00\x00")
func layoutsLayoutHtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_templatesLayoutsLayoutHtml, _layoutsLayoutHtml,
"templates/layouts/layout.html", "layouts/layout.html",
) )
} }
func templatesLayoutsLayoutHtml() (*asset, error) { func layoutsLayoutHtml() (*asset, error) {
bytes, err := templatesLayoutsLayoutHtmlBytes() bytes, err := layoutsLayoutHtmlBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "templates/layouts/layout.html", size: 180, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} info := bindataFileInfo{name: "layouts/layout.html", size: 180, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _templatesLayoutsMylayoutHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\x8f\x4d\x6a\xc5\x30\x0c\x84\xd7\x35\xf8\x0e\xd3\x03\x18\x93\xbd\xf1\x09\xba\x2a\xbd\x80\x53\xab\xc8\xe0\x9f\xe2\x28\x0b\x13\x72\xf7\x47\x9c\xbc\x95\x46\x62\x46\x7c\xe3\x58\x4a\xf6\x5a\x39\xa6\x10\xaf\x29\x49\x32\xf9\x32\xf0\x15\x46\xdb\xc5\xd9\xfb\xa0\x95\x56\xce\xbe\x4d\x6b\x8b\xc3\x6b\xf5\xe1\x78\xf1\x3f\x9c\x36\xa4\x0d\xc2\x84\x3c\x33\xf8\x6b\x7d\xae\xb6\x0c\x8b\x50\xe3\x14\x4d\x98\x3a\x7a\xdb\x85\x36\xb4\x9a\x87\xb3\xbc\xcc\x27\x6b\x87\x9d\xe2\xd3\x18\x7c\x53\x8d\x74\xc7\x7f\xf7\xde\xa9\x0a\x84\xca\x7f\x0e\x42\x60\xea\x04\x63\x2e\xef\x71\x60\x24\xca\x11\xe7\x79\x81\x3d\x40\xce\x3e\x75\x5e\x01\x00\x00\xff\xff\x64\xea\xc5\x1d\xd7\x00\x00\x00") var _layoutsMylayoutHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\x8f\x4d\x6a\xc5\x30\x0c\x84\xd7\x35\xf8\x0e\xd3\x03\x18\x93\xbd\xf1\x09\xba\x2a\xbd\x80\x53\xab\xc8\xe0\x9f\xe2\x28\x0b\x13\x72\xf7\x47\x9c\xbc\x95\x46\x62\x46\x7c\xe3\x58\x4a\xf6\x5a\x39\xa6\x10\xaf\x29\x49\x32\xf9\x32\xf0\x15\x46\xdb\xc5\xd9\xfb\xa0\x95\x56\xce\xbe\x4d\x6b\x8b\xc3\x6b\xf5\xe1\x78\xf1\x3f\x9c\x36\xa4\x0d\xc2\x84\x3c\x33\xf8\x6b\x7d\xae\xb6\x0c\x8b\x50\xe3\x14\x4d\x98\x3a\x7a\xdb\x85\x36\xb4\x9a\x87\xb3\xbc\xcc\x27\x6b\x87\x9d\xe2\xd3\x18\x7c\x53\x8d\x74\xc7\x7f\xf7\xde\xa9\x0a\x84\xca\x7f\x0e\x42\x60\xea\x04\x63\x2e\xef\x71\x60\x24\xca\x11\xe7\x79\x81\x3d\x40\xce\x3e\x75\x5e\x01\x00\x00\xff\xff\x64\xea\xc5\x1d\xd7\x00\x00\x00")
func templatesLayoutsMylayoutHtmlBytes() ([]byte, error) { func layoutsMylayoutHtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_templatesLayoutsMylayoutHtml, _layoutsMylayoutHtml,
"templates/layouts/mylayout.html", "layouts/mylayout.html",
) )
} }
func templatesLayoutsMylayoutHtml() (*asset, error) { func layoutsMylayoutHtml() (*asset, error) {
bytes, err := templatesLayoutsMylayoutHtmlBytes() bytes, err := layoutsMylayoutHtmlBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "templates/layouts/mylayout.html", size: 215, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} info := bindataFileInfo{name: "layouts/mylayout.html", size: 215, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _templatesPage1Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x3c\xca\x41\xaa\xc2\x30\x10\x00\xd0\xf5\x2f\xf4\x0e\xc3\xec\xbf\x25\x5b\x8d\x3d\x83\x37\x90\x69\x33\xa4\xa1\x63\x53\x26\x69\x40\x42\xee\x2e\xa2\xb8\x7c\xf0\xac\x0b\x05\x52\x7e\x0a\x5f\x71\xa2\x79\xf5\x1a\x8f\xcd\xfd\xcf\x51\xa2\x9e\x61\x12\x9a\xd7\x0b\xfc\x74\x30\x8e\x7d\xd7\x77\x7f\x76\x31\xe3\x8d\x3c\x83\x81\x5a\xc1\x2b\x73\x06\x0c\x1a\x12\x38\x2e\x2c\x71\x67\xc5\xd6\xec\xb0\x98\xcf\xaf\x15\x94\x37\xc7\x0a\xb8\x93\xe6\x40\x92\x86\x9d\x3c\x9b\xfb\x97\xe6\xb4\xe4\x87\x60\x6b\xef\x6e\x07\x17\xca\xd8\x77\xaf\x00\x00\x00\xff\xff\x47\x41\x4a\x5c\x9d\x00\x00\x00") var _page1Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x3c\xca\x41\xaa\xc2\x30\x10\x00\xd0\xf5\x2f\xf4\x0e\xc3\xec\xbf\x25\x5b\x8d\x3d\x83\x37\x90\x69\x33\xa4\xa1\x63\x53\x26\x69\x40\x42\xee\x2e\xa2\xb8\x7c\xf0\xac\x0b\x05\x52\x7e\x0a\x5f\x71\xa2\x79\xf5\x1a\x8f\xcd\xfd\xcf\x51\xa2\x9e\x61\x12\x9a\xd7\x0b\xfc\x74\x30\x8e\x7d\xd7\x77\x7f\x76\x31\xe3\x8d\x3c\x83\x81\x5a\xc1\x2b\x73\x06\x0c\x1a\x12\x38\x2e\x2c\x71\x67\xc5\xd6\xec\xb0\x98\xcf\xaf\x15\x94\x37\xc7\x0a\xb8\x93\xe6\x40\x92\x86\x9d\x3c\x9b\xfb\x97\xe6\xb4\xe4\x87\x60\x6b\xef\x6e\x07\x17\xca\xd8\x77\xaf\x00\x00\x00\xff\xff\x47\x41\x4a\x5c\x9d\x00\x00\x00")
func templatesPage1HtmlBytes() ([]byte, error) { func page1HtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_templatesPage1Html, _page1Html,
"templates/page1.html", "page1.html",
) )
} }
func templatesPage1Html() (*asset, error) { func page1Html() (*asset, error) {
bytes, err := templatesPage1HtmlBytes() bytes, err := page1HtmlBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "templates/page1.html", size: 157, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} info := bindataFileInfo{name: "page1.html", size: 157, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _templatesPartialsPage1_partial1Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\x49\xc9\x2c\x53\x28\x2e\xa9\xcc\x49\xb5\x55\x4a\x4a\x4c\xce\x4e\x2f\xca\x2f\xcd\x4b\xd1\x4d\xce\xcf\xc9\x2f\xb2\x52\x28\xcf\xc8\x2c\x49\xb5\x56\x80\xf2\x8a\x52\x53\x94\xec\x78\xb9\x38\x6d\x32\x0c\xed\x02\x12\xd3\x53\x15\x0c\xd5\x8b\x15\x02\x12\x8b\x4a\x32\x13\x73\x14\x0c\x6d\xf4\x33\x0c\xed\x78\xb9\x6c\xf4\x53\x32\xcb\xec\x78\xb9\x00\x01\x00\x00\xff\xff\xa2\xa6\x60\xb6\x59\x00\x00\x00") var _partialsPage1_partial1Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\x49\xc9\x2c\x53\x28\x2e\xa9\xcc\x49\xb5\x55\x4a\x4a\x4c\xce\x4e\x2f\xca\x2f\xcd\x4b\xd1\x4d\xce\xcf\xc9\x2f\xb2\x52\x28\xcf\xc8\x2c\x49\xb5\x56\x80\xf2\x8a\x52\x53\x94\xec\x78\xb9\x38\x6d\x32\x0c\xed\x02\x12\xd3\x53\x15\x0c\xd5\x8b\x15\x02\x12\x8b\x4a\x32\x13\x73\x14\x0c\x6d\xf4\x33\x0c\xed\x78\xb9\x6c\xf4\x53\x32\xcb\xec\x78\xb9\x00\x01\x00\x00\xff\xff\xa2\xa6\x60\xb6\x59\x00\x00\x00")
func templatesPartialsPage1_partial1HtmlBytes() ([]byte, error) { func partialsPage1_partial1HtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_templatesPartialsPage1_partial1Html, _partialsPage1_partial1Html,
"templates/partials/page1_partial1.html", "partials/page1_partial1.html",
) )
} }
func templatesPartialsPage1_partial1Html() (*asset, error) { func partialsPage1_partial1Html() (*asset, error) {
bytes, err := templatesPartialsPage1_partial1HtmlBytes() bytes, err := partialsPage1_partial1HtmlBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "templates/partials/page1_partial1.html", size: 89, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} info := bindataFileInfo{name: "partials/page1_partial1.html", size: 89, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -212,10 +300,10 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name. // _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){ var _bindata = map[string]func() (*asset, error){
"templates/layouts/layout.html": templatesLayoutsLayoutHtml, "layouts/layout.html": layoutsLayoutHtml,
"templates/layouts/mylayout.html": templatesLayoutsMylayoutHtml, "layouts/mylayout.html": layoutsMylayoutHtml,
"templates/page1.html": templatesPage1Html, "page1.html": page1Html,
"templates/partials/page1_partial1.html": templatesPartialsPage1_partial1Html, "partials/page1_partial1.html": partialsPage1_partial1Html,
} }
// AssetDir returns the file names below a certain // AssetDir returns the file names below a certain
@ -259,15 +347,13 @@ type bintree struct {
} }
var _bintree = &bintree{nil, map[string]*bintree{ var _bintree = &bintree{nil, map[string]*bintree{
"templates": {nil, map[string]*bintree{ "layouts": {nil, map[string]*bintree{
"layouts": {nil, map[string]*bintree{ "layout.html": {layoutsLayoutHtml, map[string]*bintree{}},
"layout.html": {templatesLayoutsLayoutHtml, map[string]*bintree{}}, "mylayout.html": {layoutsMylayoutHtml, map[string]*bintree{}},
"mylayout.html": {templatesLayoutsMylayoutHtml, map[string]*bintree{}}, }},
}}, "page1.html": {page1Html, map[string]*bintree{}},
"page1.html": {templatesPage1Html, map[string]*bintree{}}, "partials": {nil, map[string]*bintree{
"partials": {nil, map[string]*bintree{ "page1_partial1.html": {partialsPage1_partial1Html, map[string]*bintree{}},
"page1_partial1.html": {templatesPartialsPage1_partial1Html, map[string]*bintree{}},
}},
}}, }},
}} }}

View File

@ -1,24 +1,22 @@
package main package main
import ( import "github.com/kataras/iris/v12"
"github.com/kataras/iris/v12"
)
func main() { func main() {
app := iris.New() app := iris.New()
tmpl := iris.HTML("./templates", ".html") // $ go get -u github.com/go-bindata/go-bindata
// # OR: go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// # to save it to your go.mod file
// $ go-bindata -fs -prefix "templates" ./templates/...
// $ go run .
// html files are not used, you can delete the folder and run the example.
tmpl := iris.HTML(AssetFile(), ".html")
tmpl.Layout("layouts/layout.html") tmpl.Layout("layouts/layout.html")
tmpl.AddFunc("greet", func(s string) string { tmpl.AddFunc("greet", func(s string) string {
return "Greetings " + s + "!" return "Greetings " + s + "!"
}) })
// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// $ go-bindata ./templates/...
// $ go run .
// html files are not used, you can delete the folder and run the example.
tmpl.Binary(Asset, AssetNames) // <-- IMPORTANT
app.RegisterView(tmpl) app.RegisterView(tmpl)
app.Get("/", func(ctx iris.Context) { app.Get("/", func(ctx iris.Context) {
@ -54,8 +52,3 @@ func main() {
// http://localhost:8080/my/other // http://localhost:8080/my/other
app.Listen(":8080") app.Listen(":8080")
} }
// Note for new Gophers:
// `go build` is used instead of `go run main.go` as the example comments says
// otherwise you will get compile errors, this is a Go thing;
// because you have multiple files in the `package main`.

View File

@ -3,4 +3,5 @@ html
head head
title Main Page title Main Page
body body
h1 Layout
{{ yield }} {{ yield }}

View File

@ -0,0 +1,20 @@
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) {
ctx.View("index.amber", iris.Map{
"Title": "Title of The Page",
})
})
app.Listen(":8080")
}

View File

@ -0,0 +1,11 @@
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

View File

@ -0,0 +1,15 @@
!!! 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

View File

@ -0,0 +1,357 @@
// 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"
"io/ioutil"
"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 = ioutil.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, "/")...)...)
}

View File

@ -0,0 +1,30 @@
package main
import "github.com/kataras/iris/v12"
// $ go get -u github.com/go-bindata/go-bindata
// # OR: go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// # to save it to your go.mod file
//
// $ 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) {
ctx.View("index.amber", iris.Map{
"Title": "Title of The Page",
})
})
app.Listen(":8080")
}

View File

@ -1,6 +1,6 @@
// Code generated by go-bindata. (@generated) DO NOT EDIT. // Code generated by go-bindata. (@generated) DO NOT EDIT.
//Package main generated by go-bindata.// sources: // Package main generated by go-bindata.// sources:
// ../template_blocks_0/views/500.html // ../template_blocks_0/views/500.html
// ../template_blocks_0/views/index.html // ../template_blocks_0/views/index.html
// ../template_blocks_0/views/layouts/error.html // ../template_blocks_0/views/layouts/error.html
@ -14,6 +14,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -82,102 +83,188 @@ func (fi bindataFileInfo) Sys() interface{} {
return nil return nil
} }
var _views500Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x8f\xb1\x4e\xf3\x30\x14\x85\xf7\x48\x79\x87\xfb\x67\xe9\x8f\xd4\xa4\xea\x5a\x42\x37\x06\x06\xa6\x22\x21\x46\xc7\x3e\xad\x2d\x9c\x7b\x23\xdb\x49\x89\xa2\xbc\x3b\x6a\x5a\x04\x2c\xac\xf7\x7e\xe7\x3b\x3a\xf5\xbf\xb2\xa4\x37\xe9\x49\x2b\x26\x83\xa3\x63\x50\x2b\x01\x94\xac\x62\x12\x06\x35\x5e\xf4\x7b\x95\x67\x2f\x16\x17\x40\xf5\x3e\x2d\x77\x17\xa9\xd0\xc2\x09\x9c\x0a\x3a\x5b\xa7\x2d\x45\x2b\xbd\x37\xd4\x5c\xd2\xa0\x56\x39\xa6\x84\xb6\xf3\x2a\x61\x15\xa9\x11\x33\x56\x79\x76\x90\x35\x61\x00\x93\x3b\x92\x4b\xab\x48\xad\x8b\xd1\xf1\x89\xfe\x47\x80\x1c\x1b\x7c\x54\x36\xb5\xfe\x6e\x7d\x7d\x2b\x63\x60\x48\xf5\x49\x5a\x95\x9c\x56\xde\x8f\xd4\x8c\x4b\xc3\xe0\x70\x26\xf0\xc9\x31\xaa\x3c\x7b\xb5\x60\x1a\xa5\x27\x06\x0c\x25\xf9\x63\xce\x7a\xe1\xac\x1a\x70\xe1\x9a\x1b\x13\x3b\x68\x77\x74\x7a\x97\x67\x65\xb9\xcf\xb3\x69\xfa\x52\x7c\x0f\x9d\xe7\x3c\xab\xed\x76\xff\xc4\x09\x81\x95\xa7\x03\xc2\x80\x40\x8f\x21\x48\xa8\x37\x76\x7b\xcd\x81\xcd\x82\xfe\x92\xb4\x88\x51\x9d\x70\x93\x74\x14\xd3\xe8\xf1\x50\x68\xf1\x12\x76\x01\xe6\xbe\xd8\x4f\x53\xf5\x7c\xa5\xe6\xb9\xde\x74\x3f\x65\x9f\x01\x00\x00\xff\xff\xb3\xfa\x91\xbd\xaa\x01\x00\x00") type assetFile struct {
*bytes.Reader
name string
childInfos []os.FileInfo
childInfoOffset int
}
func views500HtmlBytes() ([]byte, error) { 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 __500Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x8f\xb1\x4e\xf3\x30\x14\x85\xf7\x48\x79\x87\xfb\x67\xe9\x8f\xd4\xa4\xea\x5a\x42\x37\x06\x06\xa6\x22\x21\x46\xc7\x3e\xad\x2d\x9c\x7b\x23\xdb\x49\x89\xa2\xbc\x3b\x6a\x5a\x04\x2c\xac\xf7\x7e\xe7\x3b\x3a\xf5\xbf\xb2\xa4\x37\xe9\x49\x2b\x26\x83\xa3\x63\x50\x2b\x01\x94\xac\x62\x12\x06\x35\x5e\xf4\x7b\x95\x67\x2f\x16\x17\x40\xf5\x3e\x2d\x77\x17\xa9\xd0\xc2\x09\x9c\x0a\x3a\x5b\xa7\x2d\x45\x2b\xbd\x37\xd4\x5c\xd2\xa0\x56\x39\xa6\x84\xb6\xf3\x2a\x61\x15\xa9\x11\x33\x56\x79\x76\x90\x35\x61\x00\x93\x3b\x92\x4b\xab\x48\xad\x8b\xd1\xf1\x89\xfe\x47\x80\x1c\x1b\x7c\x54\x36\xb5\xfe\x6e\x7d\x7d\x2b\x63\x60\x48\xf5\x49\x5a\x95\x9c\x56\xde\x8f\xd4\x8c\x4b\xc3\xe0\x70\x26\xf0\xc9\x31\xaa\x3c\x7b\xb5\x60\x1a\xa5\x27\x06\x0c\x25\xf9\x63\xce\x7a\xe1\xac\x1a\x70\xe1\x9a\x1b\x13\x3b\x68\x77\x74\x7a\x97\x67\x65\xb9\xcf\xb3\x69\xfa\x52\x7c\x0f\x9d\xe7\x3c\xab\xed\x76\xff\xc4\x09\x81\x95\xa7\x03\xc2\x80\x40\x8f\x21\x48\xa8\x37\x76\x7b\xcd\x81\xcd\x82\xfe\x92\xb4\x88\x51\x9d\x70\x93\x74\x14\xd3\xe8\xf1\x50\x68\xf1\x12\x76\x01\xe6\xbe\xd8\x4f\x53\xf5\x7c\xa5\xe6\xb9\xde\x74\x3f\x65\x9f\x01\x00\x00\xff\xff\xb3\xfa\x91\xbd\xaa\x01\x00\x00")
func _500HtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_views500Html, __500Html,
"views/500.html", "500.html",
) )
} }
func views500Html() (*asset, error) { func _500Html() (*asset, error) {
bytes, err := views500HtmlBytes() bytes, err := _500HtmlBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "views/500.html", size: 426, mode: os.FileMode(438), modTime: time.Unix(1596515591, 0)} info := bindataFileInfo{name: "500.html", size: 426, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _viewsIndexHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\xc9\x30\xb4\xf3\xcc\x4b\x49\xad\x50\x70\xca\x4f\xa9\xb4\xd1\xcf\x30\xb4\x03\x04\x00\x00\xff\xff\xcc\x4b\x98\x69\x13\x00\x00\x00") var _indexHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\xc9\x30\xb4\xf3\xcc\x4b\x49\xad\x50\x70\xca\x4f\xa9\xb4\xd1\xcf\x30\xb4\x03\x04\x00\x00\xff\xff\xcc\x4b\x98\x69\x13\x00\x00\x00")
func viewsIndexHtmlBytes() ([]byte, error) { func indexHtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_viewsIndexHtml, _indexHtml,
"views/index.html", "index.html",
) )
} }
func viewsIndexHtml() (*asset, error) { func indexHtml() (*asset, error) {
bytes, err := viewsIndexHtmlBytes() bytes, err := indexHtmlBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "views/index.html", size: 19, mode: os.FileMode(438), modTime: time.Unix(1596514225, 0)} info := bindataFileInfo{name: "index.html", size: 19, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _viewsLayoutsErrorHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x90\xbf\x4e\xf3\x40\x10\xc4\x7b\x4b\x7e\x87\xfd\xb6\xfe\x6c\x43\x47\x71\xe7\x26\x40\x0b\x45\x28\x28\x37\x77\xa3\xf8\xc4\xfd\x89\xe2\x55\x22\x74\xf2\xbb\xa3\x18\x83\x44\xb5\xda\x99\x9f\x66\x57\x63\xfe\x3d\xbe\xec\xf6\xef\xaf\x4f\x34\x69\x8a\x63\xdb\x98\xdb\xa4\x28\xf9\x68\x19\x99\x57\x05\xe2\xc7\xb6\x21\x22\x32\x09\x2a\xe4\x26\x39\xcf\x50\xcb\x6f\xfb\xe7\xee\x81\xff\x78\x59\x12\x2c\x5f\x02\xae\xa7\x72\x56\x26\x57\xb2\x22\xab\xe5\x6b\xf0\x3a\x59\x8f\x4b\x70\xe8\xd6\xe5\x3f\x85\x1c\x34\x48\xec\x66\x27\x11\xf6\xbe\xbf\xfb\xcd\xd2\xa0\x11\x63\xad\xfd\xae\x78\x2c\x8b\x19\xbe\x85\xb6\x31\xc3\xf6\x8e\x39\x14\xff\xb9\xe1\xb5\x92\x22\x9d\xa2\x28\x88\xb7\x8b\x4c\xfd\xb2\xb4\xcd\x0f\x70\x88\xc5\x7d\x10\x27\xcc\xb3\x1c\xb1\x9a\xb5\x22\xfb\x1b\x63\x86\x2d\xcb\x0c\x6b\x0b\x5f\x01\x00\x00\xff\xff\xbe\xb7\x11\x67\x15\x01\x00\x00") var _layoutsErrorHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x90\xbf\x4e\xf3\x40\x10\xc4\x7b\x4b\x7e\x87\xfd\xb6\xfe\x6c\x43\x47\x71\xe7\x26\x40\x0b\x45\x28\x28\x37\x77\xa3\xf8\xc4\xfd\x89\xe2\x55\x22\x74\xf2\xbb\xa3\x18\x83\x44\xb5\xda\x99\x9f\x66\x57\x63\xfe\x3d\xbe\xec\xf6\xef\xaf\x4f\x34\x69\x8a\x63\xdb\x98\xdb\xa4\x28\xf9\x68\x19\x99\x57\x05\xe2\xc7\xb6\x21\x22\x32\x09\x2a\xe4\x26\x39\xcf\x50\xcb\x6f\xfb\xe7\xee\x81\xff\x78\x59\x12\x2c\x5f\x02\xae\xa7\x72\x56\x26\x57\xb2\x22\xab\xe5\x6b\xf0\x3a\x59\x8f\x4b\x70\xe8\xd6\xe5\x3f\x85\x1c\x34\x48\xec\x66\x27\x11\xf6\xbe\xbf\xfb\xcd\xd2\xa0\x11\x63\xad\xfd\xae\x78\x2c\x8b\x19\xbe\x85\xb6\x31\xc3\xf6\x8e\x39\x14\xff\xb9\xe1\xb5\x92\x22\x9d\xa2\x28\x88\xb7\x8b\x4c\xfd\xb2\xb4\xcd\x0f\x70\x88\xc5\x7d\x10\x27\xcc\xb3\x1c\xb1\x9a\xb5\x22\xfb\x1b\x63\x86\x2d\xcb\x0c\x6b\x0b\x5f\x01\x00\x00\xff\xff\xbe\xb7\x11\x67\x15\x01\x00\x00")
func viewsLayoutsErrorHtmlBytes() ([]byte, error) { func layoutsErrorHtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_viewsLayoutsErrorHtml, _layoutsErrorHtml,
"views/layouts/error.html", "layouts/error.html",
) )
} }
func viewsLayoutsErrorHtml() (*asset, error) { func layoutsErrorHtml() (*asset, error) {
bytes, err := viewsLayoutsErrorHtmlBytes() bytes, err := layoutsErrorHtmlBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "views/layouts/error.html", size: 277, mode: os.FileMode(438), modTime: time.Unix(1596514340, 0)} info := bindataFileInfo{name: "layouts/error.html", size: 277, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _viewsLayoutsMainHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x90\xb1\x4e\xf4\x30\x10\x84\xfb\x48\x79\x87\xf9\x5d\xff\x49\xa0\xa3\xb0\xd3\x70\xd0\x21\x28\x42\x41\xb9\x24\x1b\x62\xc9\x71\xa2\x64\xb9\x13\xb2\xfc\xee\xc8\x39\x0b\xe9\x2a\xcf\xfa\xb3\x66\xc6\xab\xff\x9d\x5e\x1f\xbb\x8f\xb7\x27\x4c\x32\xbb\xb6\x2c\x74\x3a\xe1\xc8\x7f\x19\xc5\x5e\x1d\x37\x4c\x43\x5b\x16\x00\xa0\x67\x16\x42\x3f\xd1\xb6\xb3\x18\xf5\xde\x3d\x57\x0f\xea\x86\x79\x9a\xd9\xa8\xb3\xe5\xcb\xba\x6c\xa2\xd0\x2f\x5e\xd8\x8b\x51\x17\x3b\xc8\x64\x06\x3e\xdb\x9e\xab\x63\xf8\x0f\xeb\xad\x58\x72\xd5\xde\x93\x63\x73\x5f\xdf\xfd\x79\x89\x15\xc7\x6d\x08\xb0\x23\xea\x2e\x0d\x88\x31\x84\x1b\xcd\x6e\x4f\xea\xc4\x23\x7d\x3b\xc1\x0b\x59\x8f\x03\x27\xe6\x07\xc4\xa8\x9b\xab\x4f\x59\xe8\x26\xff\x42\x7f\x2e\xc3\x4f\x4e\x09\x01\xc2\xf3\xea\x48\x18\x2a\x17\x55\xa8\x11\x63\x59\x94\x85\x1e\x97\x45\x78\x4b\x25\x56\xda\x52\x4f\xa8\x2c\xf6\xe6\xca\x14\xea\x14\x92\x1f\xa6\x94\xec\xae\x9b\x63\x9d\xbf\x01\x00\x00\xff\xff\x44\x95\x63\x98\x5e\x01\x00\x00") var _layoutsMainHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x90\xb1\x4e\xf4\x30\x10\x84\xfb\x48\x79\x87\xf9\x5d\xff\x49\xa0\xa3\xb0\xd3\x70\xd0\x21\x28\x42\x41\xb9\x24\x1b\x62\xc9\x71\xa2\x64\xb9\x13\xb2\xfc\xee\xc8\x39\x0b\xe9\x2a\xcf\xfa\xb3\x66\xc6\xab\xff\x9d\x5e\x1f\xbb\x8f\xb7\x27\x4c\x32\xbb\xb6\x2c\x74\x3a\xe1\xc8\x7f\x19\xc5\x5e\x1d\x37\x4c\x43\x5b\x16\x00\xa0\x67\x16\x42\x3f\xd1\xb6\xb3\x18\xf5\xde\x3d\x57\x0f\xea\x86\x79\x9a\xd9\xa8\xb3\xe5\xcb\xba\x6c\xa2\xd0\x2f\x5e\xd8\x8b\x51\x17\x3b\xc8\x64\x06\x3e\xdb\x9e\xab\x63\xf8\x0f\xeb\xad\x58\x72\xd5\xde\x93\x63\x73\x5f\xdf\xfd\x79\x89\x15\xc7\x6d\x08\xb0\x23\xea\x2e\x0d\x88\x31\x84\x1b\xcd\x6e\x4f\xea\xc4\x23\x7d\x3b\xc1\x0b\x59\x8f\x03\x27\xe6\x07\xc4\xa8\x9b\xab\x4f\x59\xe8\x26\xff\x42\x7f\x2e\xc3\x4f\x4e\x09\x01\xc2\xf3\xea\x48\x18\x2a\x17\x55\xa8\x11\x63\x59\x94\x85\x1e\x97\x45\x78\x4b\x25\x56\xda\x52\x4f\xa8\x2c\xf6\xe6\xca\x14\xea\x14\x92\x1f\xa6\x94\xec\xae\x9b\x63\x9d\xbf\x01\x00\x00\xff\xff\x44\x95\x63\x98\x5e\x01\x00\x00")
func viewsLayoutsMainHtmlBytes() ([]byte, error) { func layoutsMainHtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_viewsLayoutsMainHtml, _layoutsMainHtml,
"views/layouts/main.html", "layouts/main.html",
) )
} }
func viewsLayoutsMainHtml() (*asset, error) { func layoutsMainHtml() (*asset, error) {
bytes, err := viewsLayoutsMainHtmlBytes() bytes, err := layoutsMainHtmlBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "views/layouts/main.html", size: 350, mode: os.FileMode(438), modTime: time.Unix(1596514155, 0)} info := bindataFileInfo{name: "layouts/main.html", size: 350, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _viewsPartialsFooterHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\xc9\x30\xb6\x73\xcb\xcf\x2f\x49\x2d\x52\x08\x48\x2c\x2a\xc9\x4c\xcc\xb1\xd1\xcf\x30\xb6\x03\x04\x00\x00\xff\xff\x08\xe6\xe9\xf8\x17\x00\x00\x00") var _partialsFooterHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\xc9\x30\xb6\x73\xcb\xcf\x2f\x49\x2d\x52\x08\x48\x2c\x2a\xc9\x4c\xcc\xb1\xd1\xcf\x30\xb6\x03\x04\x00\x00\xff\xff\x08\xe6\xe9\xf8\x17\x00\x00\x00")
func viewsPartialsFooterHtmlBytes() ([]byte, error) { func partialsFooterHtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_viewsPartialsFooterHtml, _partialsFooterHtml,
"views/partials/footer.html", "partials/footer.html",
) )
} }
func viewsPartialsFooterHtml() (*asset, error) { func partialsFooterHtml() (*asset, error) {
bytes, err := viewsPartialsFooterHtmlBytes() bytes, err := partialsFooterHtmlBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "views/partials/footer.html", size: 23, mode: os.FileMode(438), modTime: time.Unix(1596514093, 0)} info := bindataFileInfo{name: "partials/footer.html", size: 23, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -186,8 +273,8 @@ func viewsPartialsFooterHtml() (*asset, error) {
// It returns an error if the asset could not be found or // It returns an error if the asset could not be found or
// could not be loaded. // could not be loaded.
func Asset(name string) ([]byte, error) { func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok { if f, ok := _bindata[canonicalName]; ok {
a, err := f() a, err := f()
if err != nil { if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
@ -212,8 +299,8 @@ func MustAsset(name string) []byte {
// It returns an error if the asset could not be found or // It returns an error if the asset could not be found or
// could not be loaded. // could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) { func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok { if f, ok := _bindata[canonicalName]; ok {
a, err := f() a, err := f()
if err != nil { if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
@ -234,11 +321,11 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name. // _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){ var _bindata = map[string]func() (*asset, error){
"views/500.html": views500Html, "500.html": _500Html,
"views/index.html": viewsIndexHtml, "index.html": indexHtml,
"views/layouts/error.html": viewsLayoutsErrorHtml, "layouts/error.html": layoutsErrorHtml,
"views/layouts/main.html": viewsLayoutsMainHtml, "layouts/main.html": layoutsMainHtml,
"views/partials/footer.html": viewsPartialsFooterHtml, "partials/footer.html": partialsFooterHtml,
} }
// AssetDir returns the file names below a certain // AssetDir returns the file names below a certain
@ -252,13 +339,13 @@ var _bindata = map[string]func() (*asset, error){
// b.png // b.png
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error
// AssetDir("") will return []string{"data"}. // AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) { func AssetDir(name string) ([]string, error) {
node := _bintree node := _bintree
if len(name) != 0 { if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/") pathList := strings.Split(canonicalName, "/")
for _, p := range pathList { for _, p := range pathList {
node = node.Children[p] node = node.Children[p]
if node == nil { if node == nil {
@ -282,16 +369,14 @@ type bintree struct {
} }
var _bintree = &bintree{nil, map[string]*bintree{ var _bintree = &bintree{nil, map[string]*bintree{
"views": {nil, map[string]*bintree{ "500.html": {_500Html, map[string]*bintree{}},
"500.html": {views500Html, map[string]*bintree{}}, "index.html": {indexHtml, map[string]*bintree{}},
"index.html": {viewsIndexHtml, map[string]*bintree{}}, "layouts": {nil, map[string]*bintree{
"layouts": {nil, map[string]*bintree{ "error.html": {layoutsErrorHtml, map[string]*bintree{}},
"error.html": {viewsLayoutsErrorHtml, map[string]*bintree{}}, "main.html": {layoutsMainHtml, map[string]*bintree{}},
"main.html": {viewsLayoutsMainHtml, map[string]*bintree{}}, }},
}}, "partials": {nil, map[string]*bintree{
"partials": {nil, map[string]*bintree{ "footer.html": {partialsFooterHtml, map[string]*bintree{}},
"footer.html": {viewsPartialsFooterHtml, map[string]*bintree{}},
}},
}}, }},
}} }}
@ -338,6 +423,6 @@ func RestoreAssets(dir, name string) error {
} }
func _filePath(dir, name string) string { func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
} }

View File

@ -2,15 +2,21 @@ package main
import "github.com/kataras/iris/v12" import "github.com/kataras/iris/v12"
// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata // $ go get -u github.com/go-bindata/go-bindata
// $ go-bindata -prefix "../template_blocks_0" ../template_blocks_0/views/... // # OR: go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// # to save it to your go.mod file
//
// $ go-bindata -fs -prefix "../template_blocks_0/views" ../template_blocks_0/views/...
// $ go run . // $ go run .
// # OR go-bindata -prefix "../template_blocks_0/views" ../template_blocks_0/views/... with iris.Blocks("").Binary(...) //
// # OR: go-bindata -fs -prefix "views" ./views/... if the views dir is rel to the executable.
// # OR: go-bindata -fs -prefix "../template_blocks_0" ../template_blocks_0/views/...
// # with iris.Blocks(AssetFile()).RootDir("/views")
//
// System files are not used, you can optionally delete the folder and run the example now. // System files are not used, you can optionally delete the folder and run the example now.
func main() { func main() {
app := iris.New() app := iris.New()
app.RegisterView(iris.Blocks("./views", ".html").Binary(Asset, AssetNames)) app.RegisterView(iris.Blocks(AssetFile(), ".html"))
app.Get("/", index) app.Get("/", index)
app.Get("/500", internalServerError) app.Get("/500", internalServerError)

View File

@ -1,25 +0,0 @@
# Jet Engine Example (Embedded)
Take a look at the [../template_jet_0](../template_jet_0)'s README first.
This example teaches you how to use jet templates embedded in your applications with ease using the Iris built-in Jet view engine.
This example is a customized fork of https://github.com/CloudyKit/jet/tree/master/examples/asset_packaging, so you can
notice the differences side by side. For example, you don't have to use any external package inside your application,
Iris manually builds the template loader for binary data when Asset and AssetNames are available through tools like the [go-bindata](github.com/go-bindata/go-bindata).
Note that you can still use any custom loaders through the `JetEngine.SetLoader`
which overrides any previous loaders like `JetEngine.Binary` we use on this example.
## How to run
```sh
$ go get -u github.com/go-bindata/go-bindata/v3/go-bindata
$ go-bindata ./views/...
$ go build
$ ./template_jet_0_embedded
```
Repeat the above steps on any `./views` changes.
> html files are not used, only binary data. You can move or delete the `./views` folder.

View File

@ -1,5 +1,6 @@
// Code generated by go-bindata. DO NOT EDIT. // Code generated by go-bindata. (@generated) DO NOT EDIT.
// sources:
// Package main generated by go-bindata.// sources:
// views/includes/_partial.jet // views/includes/_partial.jet
// views/includes/blocks.jet // views/includes/blocks.jet
// views/index.jet // views/index.jet
@ -12,6 +13,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -21,7 +23,7 @@ import (
func bindataRead(data []byte, name string) ([]byte, error) { func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data)) gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil { if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err) return nil, fmt.Errorf("read %q: %v", name, err)
} }
var buf bytes.Buffer var buf bytes.Buffer
@ -29,7 +31,7 @@ func bindataRead(data []byte, name string) ([]byte, error) {
clErr := gz.Close() clErr := gz.Close()
if err != nil { if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err) return nil, fmt.Errorf("read %q: %v", name, err)
} }
if clErr != nil { if clErr != nil {
return nil, err return nil, err
@ -50,101 +52,198 @@ type bindataFileInfo struct {
modTime time.Time modTime time.Time
} }
// Name return file name
func (fi bindataFileInfo) Name() string { func (fi bindataFileInfo) Name() string {
return fi.name return fi.name
} }
// Size return file size
func (fi bindataFileInfo) Size() int64 { func (fi bindataFileInfo) Size() int64 {
return fi.size return fi.size
} }
// Mode return file mode
func (fi bindataFileInfo) Mode() os.FileMode { func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode return fi.mode
} }
// ModTime return file modify time
func (fi bindataFileInfo) ModTime() time.Time { func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime return fi.modTime
} }
// IsDir return file whether a directory
func (fi bindataFileInfo) IsDir() bool { func (fi bindataFileInfo) IsDir() bool {
return false return fi.mode&os.ModeDir != 0
} }
// Sys return file is sys mode
func (fi bindataFileInfo) Sys() interface{} { func (fi bindataFileInfo) Sys() interface{} {
return nil return nil
} }
var _viewsIncludes_partialJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\x29\xb0\xf3\xcc\x4b\xce\x29\x4d\x49\x4d\x51\x28\x48\x2c\x2a\xc9\x4c\xcc\xb1\xd1\x2f\xb0\xe3\xe5\x02\x04\x00\x00\xff\xff\x90\x62\x4f\xfb\x19\x00\x00\x00") type assetFile struct {
*bytes.Reader
name string
childInfos []os.FileInfo
childInfoOffset int
}
func viewsIncludes_partialJetBytes() ([]byte, error) { 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 _includes_partialJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\x29\xb0\xf3\xcc\x4b\xce\x29\x4d\x49\x4d\x51\x28\x48\x2c\x2a\xc9\x4c\xcc\xb1\xd1\x2f\xb0\xe3\xe5\x02\x04\x00\x00\xff\xff\x90\x62\x4f\xfb\x19\x00\x00\x00")
func includes_partialJetBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_viewsIncludes_partialJet, _includes_partialJet,
"views/includes/_partial.jet", "includes/_partial.jet",
) )
} }
func viewsIncludes_partialJet() (*asset, error) { func includes_partialJet() (*asset, error) {
bytes, err := viewsIncludes_partialJetBytes() bytes, err := includes_partialJetBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "views/includes/_partial.jet", size: 25, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} info := bindataFileInfo{name: "includes/_partial.jet", size: 25, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _viewsIncludesBlocksJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xaa\xae\x4e\xca\xc9\x4f\xce\x56\xc8\x4d\xcd\x2b\xd5\xd0\xac\xad\xe5\xe5\x52\x50\xb0\x29\xb0\x0b\xc9\x48\x05\x0b\x29\x40\x64\xcb\x13\x8b\x15\x32\xf3\xca\xf2\xb3\x53\x53\xf4\x6c\xf4\x0b\xec\x78\xb9\xaa\xab\x53\xf3\x52\x40\xca\x01\x01\x00\x00\xff\xff\xa0\xd9\xd9\x5d\x41\x00\x00\x00") var _includesBlocksJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xaa\xae\x4e\xca\xc9\x4f\xce\x56\xc8\x4d\xcd\x2b\xd5\xd0\xac\xad\xe5\xe5\x52\x50\xb0\x29\xb0\x0b\xc9\x48\x05\x0b\x29\x40\x64\xcb\x13\x8b\x15\x32\xf3\xca\xf2\xb3\x53\x53\xf4\x6c\xf4\x0b\xec\x78\xb9\xaa\xab\x53\xf3\x52\x40\xca\x01\x01\x00\x00\xff\xff\xa0\xd9\xd9\x5d\x41\x00\x00\x00")
func viewsIncludesBlocksJetBytes() ([]byte, error) { func includesBlocksJetBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_viewsIncludesBlocksJet, _includesBlocksJet,
"views/includes/blocks.jet", "includes/blocks.jet",
) )
} }
func viewsIncludesBlocksJet() (*asset, error) { func includesBlocksJet() (*asset, error) {
bytes, err := viewsIncludesBlocksJetBytes() bytes, err := includesBlocksJetBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "views/includes/blocks.jet", size: 65, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} info := bindataFileInfo{name: "includes/blocks.jet", size: 65, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _viewsIndexJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x64\x90\xb1\x6a\xf3\x30\x14\x85\xf7\x1f\xfe\x77\x38\xf5\xd2\x74\xa9\xc9\x5a\x8c\x87\x42\x86\x2e\x5d\xfa\x00\x45\xb6\x6e\xb0\x1a\x59\x57\xf8\x5e\xd7\x36\x22\xef\x5e\x2c\x9b\x10\xda\xed\x48\xe7\x3b\xe8\x43\x29\xd1\xac\x14\xac\xa0\xf0\x66\xe1\x51\xa5\x34\x31\x7a\xd7\x1a\x75\x1c\x9e\xbf\x48\x8b\xeb\xf5\xff\xbf\x94\x5c\x1f\x79\x50\x14\x2e\xb4\x7e\xb4\x24\x65\xe3\xb9\xbd\xc8\x8d\x58\x99\x7c\x05\xcb\xed\xd8\x53\xd0\x57\xb6\xcb\xe1\x69\xed\x80\xaa\x3b\xd6\xa7\xbe\x21\x6b\xc9\x82\x66\xd3\x47\x4f\x55\xd9\x1d\xeb\x75\x08\x54\xc1\x7c\xd7\x6b\x00\x52\x5a\x1c\x79\x8b\x9e\xc2\x78\x5b\x97\x5b\xbf\xe6\x94\x76\x83\x3b\x95\xcf\x68\x06\x75\xc6\xdf\xc9\x64\xf0\x8c\x87\x9d\x79\x3b\x9f\x66\x27\x2a\x87\xc2\x32\xc9\x3b\x6b\x3e\x66\x7e\x7f\x03\xa8\x62\xfd\xd1\xf1\x24\xe8\x78\xfa\x33\xc4\xc4\xc3\x45\x5e\xf0\x7b\x8e\xc9\x08\x02\x2b\x76\xde\xa2\xa1\xd6\x8c\x42\x70\x9a\xe1\xf0\xa8\xa0\x4c\x57\x65\xac\x37\x31\x0a\x76\xfb\xd4\x3d\xfc\x04\x00\x00\xff\xff\x28\x5a\x9d\x42\x85\x01\x00\x00") var _indexJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x64\x90\xb1\x6a\xf3\x30\x14\x85\xf7\x1f\xfe\x77\x38\xf5\xd2\x74\xa9\xc9\x5a\x8c\x87\x42\x86\x2e\x5d\xfa\x00\x45\xb6\x6e\xb0\x1a\x59\x57\xf8\x5e\xd7\x36\x22\xef\x5e\x2c\x9b\x10\xda\xed\x48\xe7\x3b\xe8\x43\x29\xd1\xac\x14\xac\xa0\xf0\x66\xe1\x51\xa5\x34\x31\x7a\xd7\x1a\x75\x1c\x9e\xbf\x48\x8b\xeb\xf5\xff\xbf\x94\x5c\x1f\x79\x50\x14\x2e\xb4\x7e\xb4\x24\x65\xe3\xb9\xbd\xc8\x8d\x58\x99\x7c\x05\xcb\xed\xd8\x53\xd0\x57\xb6\xcb\xe1\x69\xed\x80\xaa\x3b\xd6\xa7\xbe\x21\x6b\xc9\x82\x66\xd3\x47\x4f\x55\xd9\x1d\xeb\x75\x08\x54\xc1\x7c\xd7\x6b\x00\x52\x5a\x1c\x79\x8b\x9e\xc2\x78\x5b\x97\x5b\xbf\xe6\x94\x76\x83\x3b\x95\xcf\x68\x06\x75\xc6\xdf\xc9\x64\xf0\x8c\x87\x9d\x79\x3b\x9f\x66\x27\x2a\x87\xc2\x32\xc9\x3b\x6b\x3e\x66\x7e\x7f\x03\xa8\x62\xfd\xd1\xf1\x24\xe8\x78\xfa\x33\xc4\xc4\xc3\x45\x5e\xf0\x7b\x8e\xc9\x08\x02\x2b\x76\xde\xa2\xa1\xd6\x8c\x42\x70\x9a\xe1\xf0\xa8\xa0\x4c\x57\x65\xac\x37\x31\x0a\x76\xfb\xd4\x3d\xfc\x04\x00\x00\xff\xff\x28\x5a\x9d\x42\x85\x01\x00\x00")
func viewsIndexJetBytes() ([]byte, error) { func indexJetBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_viewsIndexJet, _indexJet,
"views/index.jet", "index.jet",
) )
} }
func viewsIndexJet() (*asset, error) { func indexJet() (*asset, error) {
bytes, err := viewsIndexJetBytes() bytes, err := indexJetBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "views/index.jet", size: 389, mode: os.FileMode(438), modTime: time.Unix(1594059793, 0)} info := bindataFileInfo{name: "index.jet", size: 389, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _viewsLayoutsApplicationJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x3c\x8e\xbd\xae\xc2\x30\x0c\x85\xf7\x4a\x7d\x07\xdf\x4c\x97\x01\x75\x65\x70\x3b\x00\x65\x85\xa1\x0c\x8c\x69\x6d\x11\x44\x7e\x10\x18\x89\x2a\xca\xbb\x23\xda\xc0\x64\xcb\x3e\xdf\xa7\x83\x7f\xdb\xfd\xa6\x3b\x1d\x5a\x30\xe2\x6c\x53\x16\xf8\x99\x60\xb5\x3f\xd7\x8a\xbd\x6a\xca\x02\x00\x0d\x6b\x9a\x36\x00\x74\x2c\x1a\x06\xa3\xef\x0f\x96\x5a\x1d\xbb\xdd\x72\xa5\xbe\x3f\xb9\x88\xe5\xa6\x75\x3d\x13\x31\x01\xbf\xb4\xbb\x59\xc6\x6a\xbe\x4f\xaa\xea\xe7\xc2\x3e\xd0\x98\xc9\x18\x7b\x1b\x86\x2b\x50\x18\x9e\x8e\xbd\xac\x03\x8d\xff\x8b\x94\x62\x64\x4f\x29\xcd\x64\xce\x63\x95\xab\xbe\x03\x00\x00\xff\xff\xee\xc2\x94\xa4\xbc\x00\x00\x00") var _layoutsApplicationJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x3c\x8e\xbd\xae\xc2\x30\x0c\x85\xf7\x4a\x7d\x07\xdf\x4c\x97\x01\x75\x65\x70\x3b\x00\x65\x85\xa1\x0c\x8c\x69\x6d\x11\x44\x7e\x10\x18\x89\x2a\xca\xbb\x23\xda\xc0\x64\xcb\x3e\xdf\xa7\x83\x7f\xdb\xfd\xa6\x3b\x1d\x5a\x30\xe2\x6c\x53\x16\xf8\x99\x60\xb5\x3f\xd7\x8a\xbd\x6a\xca\x02\x00\x0d\x6b\x9a\x36\x00\x74\x2c\x1a\x06\xa3\xef\x0f\x96\x5a\x1d\xbb\xdd\x72\xa5\xbe\x3f\xb9\x88\xe5\xa6\x75\x3d\x13\x31\x01\xbf\xb4\xbb\x59\xc6\x6a\xbe\x4f\xaa\xea\xe7\xc2\x3e\xd0\x98\xc9\x18\x7b\x1b\x86\x2b\x50\x18\x9e\x8e\xbd\xac\x03\x8d\xff\x8b\x94\x62\x64\x4f\x29\xcd\x64\xce\x63\x95\xab\xbe\x03\x00\x00\xff\xff\xee\xc2\x94\xa4\xbc\x00\x00\x00")
func viewsLayoutsApplicationJetBytes() ([]byte, error) { func layoutsApplicationJetBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_viewsLayoutsApplicationJet, _layoutsApplicationJet,
"views/layouts/application.jet", "layouts/application.jet",
) )
} }
func viewsLayoutsApplicationJet() (*asset, error) { func layoutsApplicationJet() (*asset, error) {
bytes, err := viewsLayoutsApplicationJetBytes() bytes, err := layoutsApplicationJetBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "views/layouts/application.jet", size: 188, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} info := bindataFileInfo{name: "layouts/application.jet", size: 188, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -153,8 +252,8 @@ func viewsLayoutsApplicationJet() (*asset, error) {
// It returns an error if the asset could not be found or // It returns an error if the asset could not be found or
// could not be loaded. // could not be loaded.
func Asset(name string) ([]byte, error) { func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok { if f, ok := _bindata[canonicalName]; ok {
a, err := f() a, err := f()
if err != nil { if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
@ -179,8 +278,8 @@ func MustAsset(name string) []byte {
// It returns an error if the asset could not be found or // It returns an error if the asset could not be found or
// could not be loaded. // could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) { func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok { if f, ok := _bindata[canonicalName]; ok {
a, err := f() a, err := f()
if err != nil { if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
@ -201,10 +300,10 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name. // _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){ var _bindata = map[string]func() (*asset, error){
"views/includes/_partial.jet": viewsIncludes_partialJet, "includes/_partial.jet": includes_partialJet,
"views/includes/blocks.jet": viewsIncludesBlocksJet, "includes/blocks.jet": includesBlocksJet,
"views/index.jet": viewsIndexJet, "index.jet": indexJet,
"views/layouts/application.jet": viewsLayoutsApplicationJet, "layouts/application.jet": layoutsApplicationJet,
} }
// AssetDir returns the file names below a certain // AssetDir returns the file names below a certain
@ -218,13 +317,13 @@ var _bindata = map[string]func() (*asset, error){
// b.png // b.png
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error
// AssetDir("") will return []string{"data"}. // AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) { func AssetDir(name string) ([]string, error) {
node := _bintree node := _bintree
if len(name) != 0 { if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/") pathList := strings.Split(canonicalName, "/")
for _, p := range pathList { for _, p := range pathList {
node = node.Children[p] node = node.Children[p]
if node == nil { if node == nil {
@ -248,15 +347,13 @@ type bintree struct {
} }
var _bintree = &bintree{nil, map[string]*bintree{ var _bintree = &bintree{nil, map[string]*bintree{
"views": {nil, map[string]*bintree{ "includes": {nil, map[string]*bintree{
"includes": {nil, map[string]*bintree{ "_partial.jet": {includes_partialJet, map[string]*bintree{}},
"_partial.jet": {viewsIncludes_partialJet, map[string]*bintree{}}, "blocks.jet": {includesBlocksJet, map[string]*bintree{}},
"blocks.jet": {viewsIncludesBlocksJet, map[string]*bintree{}}, }},
}}, "index.jet": {indexJet, map[string]*bintree{}},
"index.jet": {viewsIndexJet, map[string]*bintree{}}, "layouts": {nil, map[string]*bintree{
"layouts": {nil, map[string]*bintree{ "application.jet": {layoutsApplicationJet, map[string]*bintree{}},
"application.jet": {viewsLayoutsApplicationJet, map[string]*bintree{}},
}},
}}, }},
}} }}
@ -303,6 +400,6 @@ func RestoreAssets(dir, name string) error {
} }
func _filePath(dir, name string) string { func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
} }

View File

@ -6,29 +6,31 @@ package main
import ( import (
"os" "os"
"strings"
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
) )
// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata // $ go get -u github.com/go-bindata/go-bindata
// $ go-bindata ./views/... // # OR: go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// # to save it to your go.mod file
//
// $ go-bindata -fs -prefix "views" ./views/...
// $ go run . // $ go run .
//
// System files are not used, you can optionally delete the folder and run the example now.
func main() { func main() {
app := iris.New() app := iris.New()
tmpl := iris.Jet("./views", ".jet").Binary(Asset, AssetNames) tmpl := iris.Jet(AssetFile(), ".jet")
app.RegisterView(tmpl) app.RegisterView(tmpl)
app.Get("/", func(ctx iris.Context) { app.Get("/", func(ctx iris.Context) {
ctx.View("index.jet") ctx.View("index.jet")
}) })
port := os.Getenv("PORT") addr := ":8080"
if len(port) == 0 { if port := os.Getenv("PORT"); port != "" {
port = ":8080" addr = ":" + port
} else if !strings.HasPrefix(":", port) {
port = ":" + port
} }
app.Listen(port) app.Listen(addr)
} }

View File

@ -9,8 +9,6 @@ import (
"github.com/kataras/iris/v12/view" "github.com/kataras/iris/v12/view"
) )
// https://github.com/kataras/iris/issues/1443
func main() { func main() {
tmpl := iris.Jet("./views", ".jet") tmpl := iris.Jet("./views", ".jet")
@ -44,12 +42,12 @@ func (ViewBuiler) Asset(a view.JetArguments) reflect.Value {
func (ViewBuiler) Style(a view.JetArguments) reflect.Value { func (ViewBuiler) Style(a view.JetArguments) reflect.Value {
path := a.Get(0).String() path := a.Get(0).String()
s := fmt.Sprintf(`<link href="%v" rel="stylesheet"> `, path) s := fmt.Sprintf(`<link href="%v" rel="stylesheet">`, path)
return reflect.ValueOf(s) return reflect.ValueOf(s)
} }
func (ViewBuiler) Script(a view.JetArguments) reflect.Value { func (ViewBuiler) Script(a view.JetArguments) reflect.Value {
path := a.Get(0).String() path := a.Get(0).String()
s := fmt.Sprintf(`<script src="%v" ></script>`, path) s := fmt.Sprintf(`<script src="%v"></script>`, path)
return reflect.ValueOf(s) return reflect.ValueOf(s)
} }

View File

@ -1,5 +1,16 @@
{{ asset("./myasset.mp3")}} {{ asset("./myasset.mp3")}}
<br/> <br/>
{{ style("my-stle.css")}} {{ style("my-stle.css") | raw}}
<br/> <br/>
{{ script("my-script.js")}} {{ script("my-script.js") | raw}}
<!-- view page source: can't add a link as chrome blocks loading local source, instead
do that: -->
<code>
./myasset.mp3
<br/>
&lt;link href="my-stle.css" rel="stylesheet"&gt;
<br/>
&lt;script src="my-script.js">&lt;/script&gt;
</code>

View File

@ -1,16 +1,24 @@
// Package main shows an example of pug actions based on https://github.com/Joker/jade/tree/master/example/actions
package main package main
import "github.com/kataras/iris/v12" import "github.com/kataras/iris/v12"
type Person struct {
Name string
Age int
Emails []string
Jobs []*Job
}
type Job struct {
Employer string
Role string
}
func main() { func main() {
app := iris.New() app := iris.New()
tmpl := iris.Pug("./templates", ".pug") tmpl := iris.Pug("./templates", ".pug")
tmpl.Reload(true) // reload templates on each request (development mode)
tmpl.AddFunc("greet", func(s string) string { // add your template func here.
return "Greetings " + s + "!"
})
app.RegisterView(tmpl) app.RegisterView(tmpl)
app.Get("/", index) app.Get("/", index)
@ -20,9 +28,15 @@ func main() {
} }
func index(ctx iris.Context) { func index(ctx iris.Context) {
ctx.ViewData("pageTitle", "My Index Page") job1 := Job{Employer: "Monash B", Role: "Honorary"}
ctx.ViewData("youAreUsingJade", true) job2 := Job{Employer: "Box Hill", Role: "Head of HE"}
// Q: why need extension .pug?
// A: Because you can register more than one view engine per Iris application. person := Person{
ctx.View("index.pug") Name: "jan",
Age: 50,
Emails: []string{"jan@newmarch.name", "jan.newmarch@gmail.com"},
Jobs: []*Job{&job1, &job2},
}
ctx.View("index.pug", person)
} }

View File

@ -1,25 +1,20 @@
mixin withGo
| Generating Go html/template output.
doctype html doctype html
html(lang="en") html(lang="en")
head head
title= .pageTitle meta(charset="utf-8")
script(type='text/javascript'). title Title
if (foo) { body
bar(1 + 5) p ads
} ul
body li The name is {{.Name}}.
h1 Jade - template engine li The age is {{.Age}}.
#container.col
if .youAreUsingJade each _,_ in .Emails
p {{ greet "iris user" }} <!-- execute template funcs --> div An email is {{.}}
p You are amazing!
else | {{ with .Jobs }}
p Get on it! each _,_ in .
p. div
Jade is #[a(terse)] and simple An employer is {{.Employer}}
templating language with a and the role is {{.Role}}
#[strong focus] on performance | {{ end }}
and powerful features.
+ withGo

View File

@ -1,24 +1,20 @@
// Package main shows an example of pug actions based on https://github.com/Joker/jade/tree/master/example/actions
package main package main
import "github.com/kataras/iris/v12" import (
"html/template"
type Person struct { "github.com/kataras/iris/v12"
Name string )
Age int
Emails []string
Jobs []*Job
}
type Job struct {
Employer string
Role string
}
func main() { func main() {
app := iris.New() app := iris.New()
tmpl := iris.Pug("./templates", ".pug") tmpl := iris.Pug("./templates", ".pug")
tmpl.Reload(true) // reload templates on each request (development mode)
tmpl.AddFunc("bold", func(s string) (template.HTML, error) { // add your template func here.
return template.HTML("<b>" + s + "</b>"), nil
})
app.RegisterView(tmpl) app.RegisterView(tmpl)
app.Get("/", index) app.Get("/", index)
@ -28,15 +24,5 @@ func main() {
} }
func index(ctx iris.Context) { func index(ctx iris.Context) {
job1 := Job{Employer: "Monash B", Role: "Honorary"} ctx.View("index.pug")
job2 := Job{Employer: "Box Hill", Role: "Head of HE"}
person := Person{
Name: "jan",
Age: 50,
Emails: []string{"jan@newmarch.name", "jan.newmarch@gmail.com"},
Jobs: []*Job{&job1, &job2},
}
ctx.View("index.pug", person)
} }

View File

@ -1,20 +1,7 @@
doctype html doctype html
html(lang="en") html
head include header.pug
meta(charset="utf-8") body
title Title h1 My Site
body p {{ bold "Welcome to my super lame site."}}
p ads include footer.pug
ul
li The name is {{.Name}}.
li The age is {{.Age}}.
each _,_ in .Emails
div An email is {{.}}
| {{ with .Jobs }}
each _,_ in .
div
An employer is {{.Employer}}
and the role is {{.Role}}
| {{ end }}

View File

@ -1,28 +0,0 @@
package main
import (
"html/template"
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
tmpl := iris.Pug("./templates", ".pug")
tmpl.Reload(true) // reload templates on each request (development mode)
tmpl.AddFunc("bold", func(s string) (template.HTML, error) { // add your template func here.
return template.HTML("<b>" + s + "</b>"), nil
})
app.RegisterView(tmpl)
app.Get("/", index)
// http://localhost:8080
app.Listen(":8080")
}
func index(ctx iris.Context) {
ctx.View("index.pug")
}

View File

@ -1,7 +0,0 @@
doctype html
html
include header.pug
body
h1 My Site
p {{ bold "Welcome to my super lame site."}}
include footer.pug

View File

@ -1,5 +1,6 @@
// Code generated for package main by go-bindata DO NOT EDIT. (@generated) // Code generated by go-bindata. (@generated) DO NOT EDIT.
// sources:
// Package main generated by go-bindata.// sources:
// templates/index.pug // templates/index.pug
// templates/layout.pug // templates/layout.pug
package main package main
@ -10,6 +11,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -19,7 +21,7 @@ import (
func bindataRead(data []byte, name string) ([]byte, error) { func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data)) gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil { if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err) return nil, fmt.Errorf("read %q: %v", name, err)
} }
var buf bytes.Buffer var buf bytes.Buffer
@ -27,7 +29,7 @@ func bindataRead(data []byte, name string) ([]byte, error) {
clErr := gz.Close() clErr := gz.Close()
if err != nil { if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err) return nil, fmt.Errorf("read %q: %v", name, err)
} }
if clErr != nil { if clErr != nil {
return nil, err return nil, err
@ -63,7 +65,7 @@ func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode return fi.mode
} }
// Mode return file modify time // ModTime return file modify time
func (fi bindataFileInfo) ModTime() time.Time { func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime return fi.modTime
} }
@ -78,42 +80,128 @@ func (fi bindataFileInfo) Sys() interface{} {
return nil return nil
} }
var _templatesIndexPug = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xad\x28\x49\xcd\x4b\x29\x56\xc8\x49\xac\xcc\x2f\x2d\xd1\x2b\x28\x4d\xe7\xe5\xe2\xe5\x4a\xca\xc9\x4f\xce\x56\x28\xc9\x2c\xc9\x49\xe5\xe5\x52\x80\x30\x14\x1c\x8b\x4a\x32\x93\x73\x52\x15\x42\x20\xc2\x30\x55\xc9\xf9\x79\x25\xa9\x79\x25\x20\x75\x19\x86\x0a\xbe\x95\x30\x75\x80\x00\x00\x00\xff\xff\xa6\xfd\x18\x8c\x5a\x00\x00\x00") type assetFile struct {
*bytes.Reader
name string
childInfos []os.FileInfo
childInfoOffset int
}
func templatesIndexPugBytes() ([]byte, error) { 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 _indexPug = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xad\x28\x49\xcd\x4b\x29\x56\xc8\x49\xac\xcc\x2f\x2d\xd1\x2b\x28\x4d\xe7\xe5\xe2\xe5\x4a\xca\xc9\x4f\xce\x56\x28\xc9\x2c\xc9\x49\xe5\xe5\x52\x80\x30\x14\x1c\x8b\x4a\x32\x93\x73\x52\x15\x42\x20\xc2\x30\x55\xc9\xf9\x79\x25\xa9\x79\x25\x20\x75\x19\x86\x0a\xbe\x95\x30\x75\x80\x00\x00\x00\xff\xff\xa6\xfd\x18\x8c\x5a\x00\x00\x00")
func indexPugBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_templatesIndexPug, _indexPug,
"templates/index.pug", "index.pug",
) )
} }
func templatesIndexPug() (*asset, error) { func indexPug() (*asset, error) {
bytes, err := templatesIndexPugBytes() bytes, err := indexPugBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "templates/index.pug", size: 90, mode: os.FileMode(438), modTime: time.Unix(1581790962, 0)} info := bindataFileInfo{name: "index.pug", size: 90, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _templatesLayoutPug = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xc9\x4f\x2e\xa9\x2c\x48\x55\xc8\x28\xc9\xcd\xe1\xe5\x82\x90\x0a\x0a\x19\xa9\x89\x29\x20\x5a\x41\x21\x29\x27\x3f\x39\x5b\xa1\x24\xb3\x24\x27\x15\x22\xa0\x00\xe1\x28\xb8\xa4\xa6\x25\x96\xe6\x94\x20\xa4\x92\xf2\x53\x2a\x91\xf5\x24\xe7\xe7\x95\xa4\xe6\x95\x00\x02\x00\x00\xff\xff\x5f\xa5\x93\xf9\x61\x00\x00\x00") var _layoutPug = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xc9\x4f\x2e\xa9\x2c\x48\x55\xc8\x28\xc9\xcd\xe1\xe5\x82\x90\x0a\x0a\x19\xa9\x89\x29\x20\x5a\x41\x21\x29\x27\x3f\x39\x5b\xa1\x24\xb3\x24\x27\x15\x22\xa0\x00\xe1\x28\xb8\xa4\xa6\x25\x96\xe6\x94\x20\xa4\x92\xf2\x53\x2a\x91\xf5\x24\xe7\xe7\x95\xa4\xe6\x95\x00\x02\x00\x00\xff\xff\x5f\xa5\x93\xf9\x61\x00\x00\x00")
func templatesLayoutPugBytes() ([]byte, error) { func layoutPugBytes() ([]byte, error) {
return bindataRead( return bindataRead(
_templatesLayoutPug, _layoutPug,
"templates/layout.pug", "layout.pug",
) )
} }
func templatesLayoutPug() (*asset, error) { func layoutPug() (*asset, error) {
bytes, err := templatesLayoutPugBytes() bytes, err := layoutPugBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "templates/layout.pug", size: 97, mode: os.FileMode(438), modTime: time.Unix(1581790962, 0)} info := bindataFileInfo{name: "layout.pug", size: 97, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -122,8 +210,8 @@ func templatesLayoutPug() (*asset, error) {
// It returns an error if the asset could not be found or // It returns an error if the asset could not be found or
// could not be loaded. // could not be loaded.
func Asset(name string) ([]byte, error) { func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok { if f, ok := _bindata[canonicalName]; ok {
a, err := f() a, err := f()
if err != nil { if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
@ -148,8 +236,8 @@ func MustAsset(name string) []byte {
// It returns an error if the asset could not be found or // It returns an error if the asset could not be found or
// could not be loaded. // could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) { func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok { if f, ok := _bindata[canonicalName]; ok {
a, err := f() a, err := f()
if err != nil { if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
@ -170,8 +258,8 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name. // _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){ var _bindata = map[string]func() (*asset, error){
"templates/index.pug": templatesIndexPug, "index.pug": indexPug,
"templates/layout.pug": templatesLayoutPug, "layout.pug": layoutPug,
} }
// AssetDir returns the file names below a certain // AssetDir returns the file names below a certain
@ -185,13 +273,13 @@ var _bindata = map[string]func() (*asset, error){
// b.png // b.png
// then AssetDir("data") would return []string{"foo.txt", "img"} // then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("foo.txt") and AssetDir("nonexistent") would return an error
// AssetDir("") will return []string{"data"}. // AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) { func AssetDir(name string) ([]string, error) {
node := _bintree node := _bintree
if len(name) != 0 { if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/") pathList := strings.Split(canonicalName, "/")
for _, p := range pathList { for _, p := range pathList {
node = node.Children[p] node = node.Children[p]
if node == nil { if node == nil {
@ -215,10 +303,8 @@ type bintree struct {
} }
var _bintree = &bintree{nil, map[string]*bintree{ var _bintree = &bintree{nil, map[string]*bintree{
"templates": {nil, map[string]*bintree{ "index.pug": {indexPug, map[string]*bintree{}},
"index.pug": {templatesIndexPug, map[string]*bintree{}}, "layout.pug": {layoutPug, map[string]*bintree{}},
"layout.pug": {templatesLayoutPug, map[string]*bintree{}},
}},
}} }}
// RestoreAsset restores an asset under the given directory // RestoreAsset restores an asset under the given directory
@ -264,6 +350,6 @@ func RestoreAssets(dir, name string) error {
} }
func _filePath(dir, name string) string { func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1) canonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
} }

View File

@ -0,0 +1,28 @@
package main
import "github.com/kataras/iris/v12"
// $ go get -u github.com/go-bindata/go-bindata
// # OR: go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// # to save it to your go.mod file
//
// $ go-bindata -fs -prefix "templates" ./templates/...
// $ go run .
//
// System files are not used, you can optionally delete the folder and run the example now.
func main() {
app := iris.New()
tmpl := iris.Pug(AssetFile(), ".pug")
app.RegisterView(tmpl)
app.Get("/", index)
// http://localhost:8080
app.Listen(":8080")
}
func index(ctx iris.Context) {
ctx.View("index.pug")
}

View File

@ -1,23 +0,0 @@
package main
import "github.com/kataras/iris/v12"
// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata
// $ go-bindata ./templates/...
// $ go build
func main() {
app := iris.New()
tmpl := iris.Pug("./templates", ".pug").Binary(Asset, AssetNames)
app.RegisterView(tmpl)
app.Get("/", index)
// http://localhost:8080
app.Listen(":8080")
}
func index(ctx iris.Context) {
ctx.View("index.pug")
}

View File

@ -475,13 +475,28 @@ func (api *APIBuilder) HandleMany(methodOrMulti string, relativePathorMulti stri
// //
// Returns all the registered routes, including GET index and path patterm and HEAD. // Returns all the registered routes, including GET index and path patterm and HEAD.
// //
// Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/file-server // Usage:
func (api *APIBuilder) HandleDir(requestPath string, fs http.FileSystem, opts ...DirOptions) (routes []*Route) { // HandleDir("/public", "./assets", DirOptions{...}) or
// HandleDir("/public", iris.Dir("./assets"), DirOptions{...})
//
// Examples:
// https://github.com/kataras/iris/tree/master/_examples/file-server
func (api *APIBuilder) HandleDir(requestPath string, fsOrDir interface{}, opts ...DirOptions) (routes []*Route) {
options := DefaultDirOptions options := DefaultDirOptions
if len(opts) > 0 { if len(opts) > 0 {
options = opts[0] options = opts[0]
} }
var fs http.FileSystem
switch v := fsOrDir.(type) {
case string:
fs = http.Dir(v)
case http.FileSystem:
fs = v
default:
panic(fmt.Errorf(`unexpected "fsOrDir" argument type of %T (string or http.FileSystem)`, v))
}
h := FileServer(fs, options) h := FileServer(fs, options)
description := "file server" description := "file server"
if d, ok := fs.(http.Dir); ok { if d, ok := fs.(http.Dir); ok {

View File

@ -1,8 +1,6 @@
package router package router
import ( import (
"net/http"
"github.com/kataras/iris/v12/context" "github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/macro" "github.com/kataras/iris/v12/macro"
@ -201,8 +199,13 @@ type Party interface {
// //
// Returns all the registered routes, including GET index and path patterm and HEAD. // Returns all the registered routes, including GET index and path patterm and HEAD.
// //
// Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/file-server // Usage:
HandleDir(requestPath string, fs http.FileSystem, opts ...DirOptions) []*Route // HandleDir("/public", "./assets", DirOptions{...}) or
// HandleDir("/public", iris.Dir("./assets"), DirOptions{...})
//
// Examples:
// https://github.com/kataras/iris/tree/master/_examples/file-server
HandleDir(requestPath string, fileSystem interface{}, opts ...DirOptions) []*Route
// None registers an "offline" route // None registers an "offline" route
// see context.ExecRoute(routeName) and // see context.ExecRoute(routeName) and

2
go.mod
View File

@ -19,7 +19,7 @@ require (
github.com/iris-contrib/pongo2 v0.0.1 github.com/iris-contrib/pongo2 v0.0.1
github.com/iris-contrib/schema v0.0.6 github.com/iris-contrib/schema v0.0.6
github.com/json-iterator/go v1.1.10 github.com/json-iterator/go v1.1.10
github.com/kataras/blocks v0.0.2 github.com/kataras/blocks v0.0.3
github.com/kataras/golog v0.1.2 github.com/kataras/golog v0.1.2
github.com/kataras/neffos v0.0.16 github.com/kataras/neffos v0.0.16
github.com/kataras/pio v0.0.10 github.com/kataras/pio v0.0.10

View File

@ -115,11 +115,19 @@ func hi(ctx iris.Context) {
## Embedded ## Embedded
View engine supports bundled(https://github.com/go-bindata/go-bindata) template files too. View engine supports bundled(https://github.com/go-bindata/go-bindata) template files too. Latest
`go-bindata` gives you two functions, `Assset` and `AssetNames`, `go-bindata` release gives you a compatible `http.FileSystem` that can be provided as the first argument of a view engine's initialization, e.g. `HTML(AssetFile(), ".html")`.
these can be set to each of the template engines using the `.Binary` function.
Example code:
```sh
$ go get -u github.com/go-bindata/go-bindata
# OR: go get -u github.com/go-bindata/go-bindata/v3/go-bindata
# to save it to your go.mod file
$ go-bindata -fs -prefix "templates" ./templates/...
$ go run .
```
Example Code:
```go ```go
package main package main
@ -128,12 +136,7 @@ import "github.com/kataras/iris/v12"
func main() { func main() {
app := iris.New() app := iris.New()
// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata app.RegisterView(iris.HTML(AssetFile(), ".html"))
// $ go-bindata ./templates/...
// $ go build
// $ ./embedding-templates-into-app
// html files are not used, you can delete the folder and run the example
app.RegisterView(iris.HTML("./templates", ".html").Binary(Asset, AssetNames))
app.Get("/", hi) app.Get("/", hi)
// http://localhost:8080 // http://localhost:8080

View File

@ -1,7 +1,6 @@
package view package view
import ( import (
"path"
"sync" "sync"
"github.com/yosssi/ace" "github.com/yosssi/ace"
@ -13,8 +12,13 @@ import (
// The given "extension" MUST begin with a dot. // The given "extension" MUST begin with a dot.
// //
// Read more about the Ace Go Parser: https://github.com/yosssi/ace // Read more about the Ace Go Parser: https://github.com/yosssi/ace
func Ace(directory, extension string) *HTMLEngine { //
s := HTML(directory, extension) // Usage:
// Ace("./views", ".ace") or
// Ace(iris.Dir("./views"), ".ace") or
// Ace(AssetFile(), ".ace") for embedded data.
func Ace(fs interface{}, extension string) *HTMLEngine {
s := HTML(fs, extension)
funcs := make(map[string]interface{}, 0) funcs := make(map[string]interface{}, 0)
@ -30,7 +34,7 @@ func Ace(directory, extension string) *HTMLEngine {
} }
}) })
name = path.Join(path.Clean(directory), name) // name = path.Join(path.Clean(directory), name)
src := ace.NewSource( src := ace.NewSource(
ace.NewFile(name, text), ace.NewFile(name, text),

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"html/template" "html/template"
"io" "io"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -14,11 +15,10 @@ import (
// AmberEngine contains the amber view engine structure. // AmberEngine contains the amber view engine structure.
type AmberEngine struct { type AmberEngine struct {
fs http.FileSystem
// files configuration // files configuration
directory string rootDir string
extension string extension string
assetFn func(name string) ([]byte, error) // for embedded, in combination with directory & extension
namesFn func() []string // for embedded, in combination with directory & extension
reload bool reload bool
// //
rmu sync.RWMutex // locks for `ExecuteWiter` when `reload` is true. rmu sync.RWMutex // locks for `ExecuteWiter` when `reload` is true.
@ -33,9 +33,15 @@ var (
// Amber creates and returns a new amber view engine. // Amber creates and returns a new amber view engine.
// The given "extension" MUST begin with a dot. // The given "extension" MUST begin with a dot.
func Amber(directory, extension string) *AmberEngine { //
// Usage:
// Amber("./views", ".amber") or
// Amber(iris.Dir("./views"), ".amber") or
// Amber(AssetFile(), ".amber") for embedded data.
func Amber(fs interface{}, extension string) *AmberEngine {
s := &AmberEngine{ s := &AmberEngine{
directory: directory, fs: getFS(fs),
rootDir: "/",
extension: extension, extension: extension,
templateCache: make(map[string]*template.Template), templateCache: make(map[string]*template.Template),
funcs: make(map[string]interface{}), funcs: make(map[string]interface{}),
@ -44,20 +50,18 @@ func Amber(directory, extension string) *AmberEngine {
return s 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 {
s.rootDir = filepath.ToSlash(root)
return s
}
// Ext returns the file extension which this view engine is responsible to render. // Ext returns the file extension which this view engine is responsible to render.
func (s *AmberEngine) Ext() string { func (s *AmberEngine) Ext() string {
return s.extension return s.extension
} }
// Binary optionally, use it when template files are distributed
// inside the app executable (.go generated files).
//
// The assetFn and namesFn can come from the go-bindata library.
func (s *AmberEngine) Binary(assetFn func(name string) ([]byte, error), namesFn func() []string) *AmberEngine {
s.assetFn, s.namesFn = assetFn, namesFn
return s
}
// Reload if set to true the templates are reloading on each render, // 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 // use it when you're in development and you're boring of restarting
// the whole app when you edit a template file. // the whole app when you edit a template file.
@ -86,32 +90,8 @@ func (s *AmberEngine) AddFunc(funcName string, funcBody interface{}) {
// //
// Returns an error if something bad happens, user is responsible to catch it. // Returns an error if something bad happens, user is responsible to catch it.
func (s *AmberEngine) Load() error { func (s *AmberEngine) Load() error {
if s.assetFn != nil && s.namesFn != nil { s.rmu.Lock()
// embedded defer s.rmu.Unlock()
return s.loadAssets()
}
// load from directory, make the dir absolute here too.
dir, err := filepath.Abs(s.directory)
if err != nil {
return err
}
if _, err := os.Stat(dir); os.IsNotExist(err) {
return err
}
// change the directory field configuration, load happens after directory has been set, so we will not have any problems here.
s.directory = dir
return s.loadDirectory()
}
// loadDirectory loads the amber templates from directory.
func (s *AmberEngine) loadDirectory() error {
dir, extension := s.directory, s.extension
opt := amber.DirOptions{}
opt.Recursive = true
// prepare the global amber funcs // prepare the global amber funcs
funcs := template.FuncMap{} funcs := template.FuncMap{}
@ -125,84 +105,52 @@ func (s *AmberEngine) loadDirectory() error {
} }
amber.FuncMap = funcs // set the funcs amber.FuncMap = funcs // set the funcs
opt.Ext = extension
templates, err := amber.CompileDir(dir, opt, amber.DefaultOptions) // this returns the map with stripped extension, we want extension so we copy the map opts := amber.Options{
if err == nil { PrettyPrint: false,
s.templateCache = make(map[string]*template.Template) LineNumbers: false,
for k, v := range templates { VirtualFilesystem: s.fs,
name := filepath.ToSlash(k + opt.Ext)
s.templateCache[name] = v
delete(templates, k)
}
}
return err
}
// loadAssets builds the templates by binary, embedded.
func (s *AmberEngine) loadAssets() error {
virtualDirectory, virtualExtension := s.directory, s.extension
assetFn, namesFn := s.assetFn, s.namesFn
// prepare the global amber funcs
funcs := template.FuncMap{}
for k, v := range amber.FuncMap { // add the amber's default funcs
funcs[k] = v
} }
for k, v := range s.funcs { return walk(s.fs, s.rootDir, func(path string, info os.FileInfo, err error) error {
funcs[k] = v if info == nil || info.IsDir() {
} return nil
if len(virtualDirectory) > 0 {
if virtualDirectory[0] == '.' { // first check for .wrong
virtualDirectory = virtualDirectory[1:]
} }
if virtualDirectory[0] == '/' || virtualDirectory[0] == filepath.Separator { // second check for /something, (or ./something if we had dot on 0 it will be removed
virtualDirectory = virtualDirectory[1:]
}
}
amber.FuncMap = funcs // set the funcs
names := namesFn() if s.extension != "" {
if !strings.HasSuffix(path, s.extension) {
for _, path := range names { return nil
if !strings.HasPrefix(path, virtualDirectory) {
continue
}
ext := filepath.Ext(path)
if ext == virtualExtension {
rel, err := filepath.Rel(virtualDirectory, path)
if err != nil {
return err
} }
buf, err := assetFn(path)
if err != nil {
return err
}
name := filepath.ToSlash(rel)
tmpl, err := amber.CompileData(buf, name, amber.DefaultOptions)
if err != nil {
return err
}
s.templateCache[name] = tmpl
} }
}
return nil buf, err := asset(s.fs, path)
if err != nil {
return fmt.Errorf("%s: %w", path, err)
}
name := strings.TrimPrefix(path, "/")
tmpl, err := amber.CompileData(buf, name, opts)
if err != nil {
return fmt.Errorf("%s: %w", path, err)
}
s.templateCache[name] = tmpl
return nil
})
} }
func (s *AmberEngine) fromCache(relativeName string) *template.Template { func (s *AmberEngine) fromCache(relativeName string) *template.Template {
tmpl, ok := s.templateCache[relativeName] if s.reload {
if ok { s.rmu.RLock()
defer s.rmu.RUnlock()
}
if tmpl, ok := s.templateCache[relativeName]; ok {
return tmpl return tmpl
} }
return nil return nil
} }
@ -211,10 +159,6 @@ func (s *AmberEngine) fromCache(relativeName string) *template.Template {
func (s *AmberEngine) ExecuteWriter(w io.Writer, filename string, layout string, bindingData interface{}) error { func (s *AmberEngine) ExecuteWriter(w io.Writer, filename string, layout string, bindingData interface{}) error {
// re-parse the templates if reload is enabled. // re-parse the templates if reload is enabled.
if s.reload { if s.reload {
// locks to fix #872, it's the simplest solution and the most correct,
// to execute writers with "wait list", one at a time.
s.rmu.Lock()
defer s.rmu.Unlock()
if err := s.Load(); err != nil { if err := s.Load(); err != nil {
return err return err
} }
@ -224,5 +168,5 @@ func (s *AmberEngine) ExecuteWriter(w io.Writer, filename string, layout string,
return tmpl.Execute(w, bindingData) return tmpl.Execute(w, bindingData)
} }
return fmt.Errorf("Template with name: %s does not exist in the dir: %s", filename, s.directory) return ErrNotExist{filename, false}
} }

View File

@ -39,8 +39,28 @@ func WrapBlocks(v *blocks.Blocks) *BlocksEngine {
// The given "extension" MUST begin with a dot. // The given "extension" MUST begin with a dot.
// //
// See `WrapBlocks` package-level function too. // See `WrapBlocks` package-level function too.
func Blocks(directory, extension string) *BlocksEngine { //
return WrapBlocks(blocks.New(directory).Extension(extension)) // Usage:
// Blocks("./views", ".html") or
// Blocks(iris.Dir("./views"), ".html") or
// Blocks(AssetFile(), ".html") for embedded data.
func Blocks(fs interface{}, extension string) *BlocksEngine {
return WrapBlocks(blocks.New(fs).Extension(extension))
}
// RootDir sets the directory to use as the root one inside the provided File System.
func (s *BlocksEngine) RootDir(root string) *BlocksEngine {
s.Engine.RootDir(root)
return s
}
// LayoutDir sets a custom layouts directory,
// always relative to the "rootDir" one.
// Layouts are recognised by their prefix names.
// Defaults to "layouts".
func (s *BlocksEngine) LayoutDir(relToDirLayoutDir string) *BlocksEngine {
s.Engine.LayoutDir(relToDirLayoutDir)
return s
} }
// Ext returns empty ext as this template engine // Ext returns empty ext as this template engine
@ -66,18 +86,6 @@ func (s *BlocksEngine) AddLayoutFunc(funcName string, funcBody interface{}) *Blo
return s return s
} }
// Binary sets the function which reads contents based on a filename
// and a function that returns all the filenames.
// These functions are used to parse the corresponding files into templates.
// You do not need to set them when the given "rootDir" was a system directory.
// It's mostly useful when the application contains embedded template files,
// e.g. pass go-bindata's `Asset` and `AssetNames` functions
// to load templates from go-bindata generated content.
func (s *BlocksEngine) Binary(asset blocks.AssetFunc, assetNames blocks.AssetNamesFunc) *BlocksEngine {
s.Engine.Assets(asset, assetNames)
return s
}
// Layout sets the default layout which inside should use // Layout sets the default layout which inside should use
// the {{ template "content" . }} to render the main template. // the {{ template "content" . }} to render the main template.
// //

View File

@ -2,9 +2,8 @@ package view
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"io/ioutil" "net/http"
"os" "os"
stdPath "path" stdPath "path"
"path/filepath" "path/filepath"
@ -60,41 +59,40 @@ var AsValue = pongo2.AsValue
var AsSafeValue = pongo2.AsSafeValue var AsSafeValue = pongo2.AsSafeValue
type tDjangoAssetLoader struct { type tDjangoAssetLoader struct {
baseDir string rootDir string
assetGet func(name string) ([]byte, error) fs http.FileSystem
} }
// Abs calculates the path to a given template. Whenever a path must be resolved // Abs calculates the path to a given template. Whenever a path must be resolved
// due to an import from another template, the base equals the parent template's path. // due to an import from another template, the base equals the parent template's path.
func (dal *tDjangoAssetLoader) Abs(base, name string) string { func (l *tDjangoAssetLoader) Abs(base, name string) string {
if stdPath.IsAbs(name) { if stdPath.IsAbs(name) {
return name return name
} }
return stdPath.Join(dal.baseDir, name) return stdPath.Join(l.rootDir, name)
} }
// Get returns an io.Reader where the template's content can be read from. // Get returns an io.Reader where the template's content can be read from.
func (dal *tDjangoAssetLoader) Get(path string) (io.Reader, error) { func (l *tDjangoAssetLoader) Get(path string) (io.Reader, error) {
if stdPath.IsAbs(path) { if stdPath.IsAbs(path) {
path = path[1:] path = path[1:]
} }
res, err := dal.assetGet(path) res, err := asset(l.fs, path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bytes.NewBuffer(res), nil return bytes.NewReader(res), nil
} }
// DjangoEngine contains the django view engine structure. // DjangoEngine contains the django view engine structure.
type DjangoEngine struct { type DjangoEngine struct {
fs http.FileSystem
// files configuration // files configuration
directory string rootDir string
extension string extension string
assetFn func(name string) ([]byte, error) // for embedded, in combination with directory & extension
namesFn func() []string // for embedded, in combination with directory & extension
reload bool reload bool
// //
rmu sync.RWMutex // locks for filters, globals and `ExecuteWiter` when `reload` is true. rmu sync.RWMutex // locks for filters, globals and `ExecuteWiter` when `reload` is true.
@ -102,7 +100,6 @@ type DjangoEngine struct {
filters map[string]FilterFunction filters map[string]FilterFunction
// globals share context fields between templates. // globals share context fields between templates.
globals map[string]interface{} globals map[string]interface{}
mu sync.Mutex // locks for template cache
templateCache map[string]*pongo2.Template templateCache map[string]*pongo2.Template
} }
@ -113,9 +110,15 @@ var (
// Django creates and returns a new django view engine. // Django creates and returns a new django view engine.
// The given "extension" MUST begin with a dot. // The given "extension" MUST begin with a dot.
func Django(directory, extension string) *DjangoEngine { //
// Usage:
// Django("./views", ".html") or
// Django(iris.Dir("./views"), ".html") or
// Django(AssetFile(), ".html") for embedded data.
func Django(fs interface{}, extension string) *DjangoEngine {
s := &DjangoEngine{ s := &DjangoEngine{
directory: directory, fs: getFS(fs),
rootDir: "/",
extension: extension, extension: extension,
globals: make(map[string]interface{}), globals: make(map[string]interface{}),
filters: make(map[string]FilterFunction), filters: make(map[string]FilterFunction),
@ -125,20 +128,18 @@ func Django(directory, extension string) *DjangoEngine {
return s return s
} }
// RootDir sets the directory to be used as a starting point
// to load templates from the provided file system.
func (s *DjangoEngine) RootDir(root string) *DjangoEngine {
s.rootDir = filepath.ToSlash(root)
return s
}
// Ext returns the file extension which this view engine is responsible to render. // Ext returns the file extension which this view engine is responsible to render.
func (s *DjangoEngine) Ext() string { func (s *DjangoEngine) Ext() string {
return s.extension return s.extension
} }
// Binary optionally, use it when template files are distributed
// inside the app executable (.go generated files).
//
// The assetFn and namesFn can come from the go-bindata library.
func (s *DjangoEngine) Binary(assetFn func(name string) ([]byte, error), namesFn func() []string) *DjangoEngine {
s.assetFn, s.namesFn = assetFn, namesFn
return s
}
// Reload if set to true the templates are reloading on each render, // 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 // use it when you're in development and you're boring of restarting
// the whole app when you edit a template file. // the whole app when you edit a template file.
@ -203,133 +204,32 @@ func (s *DjangoEngine) RegisterTag(tagName string, fn TagParser) error {
// //
// Returns an error if something bad happens, user is responsible to catch it. // Returns an error if something bad happens, user is responsible to catch it.
func (s *DjangoEngine) Load() error { func (s *DjangoEngine) Load() error {
if s.assetFn != nil && s.namesFn != nil { set := pongo2.NewSet("", &tDjangoAssetLoader{fs: s.fs, rootDir: s.rootDir})
// embedded
return s.loadAssets()
}
// load from directory, make the dir absolute here too.
dir, err := filepath.Abs(s.directory)
if err != nil {
return err
}
if _, err := os.Stat(dir); os.IsNotExist(err) {
return err
}
// change the directory field configuration, load happens after directory has been set, so we will not have any problems here.
s.directory = dir
return s.loadDirectory()
}
// LoadDirectory loads the templates from directory.
func (s *DjangoEngine) loadDirectory() (templateErr error) {
dir, extension := s.directory, s.extension
fsLoader, err := pongo2.NewLocalFileSystemLoader(dir) // I see that this doesn't read the content if already parsed, so do it manually via filepath.Walk
if err != nil {
return err
}
set := pongo2.NewSet("", fsLoader)
set.Globals = getPongoContext(s.globals) set.Globals = getPongoContext(s.globals)
s.mu.Lock() s.rmu.Lock()
defer s.mu.Unlock() defer s.rmu.Unlock()
// Walk the supplied directory and compile any files that match our extension list. return walk(s.fs, s.rootDir, func(path string, info os.FileInfo, err error) error {
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
// Fix same-extension-dirs bug: some dir might be named to: "users.tmpl", "local.html".
// These dirs should be excluded as they are not valid golang templates, but files under
// them should be treat as normal.
// If is a dir, return immediately (dir is not a valid golang template).
if info == nil || info.IsDir() { if info == nil || info.IsDir() {
} else { return nil
}
rel, err := filepath.Rel(dir, path) if s.extension != "" {
if err != nil { if !strings.HasSuffix(path, s.extension) {
templateErr = err return nil
return err
} }
ext := filepath.Ext(rel)
if ext == extension {
buf, err := ioutil.ReadFile(path)
if err != nil {
templateErr = err
return err
}
name := filepath.ToSlash(rel)
s.templateCache[name], templateErr = set.FromString(string(buf))
if templateErr != nil {
return templateErr
}
}
}
return nil
})
if err != nil {
return err
}
return
}
// loadAssets loads the templates by binary (go-bindata for embedded).
func (s *DjangoEngine) loadAssets() error {
virtualDirectory, virtualExtension := s.directory, s.extension
assetFn, namesFn := s.assetFn, s.namesFn
// Make a file set with a template loader based on asset function
set := pongo2.NewSet("", &tDjangoAssetLoader{baseDir: s.directory, assetGet: s.assetFn})
set.Globals = getPongoContext(s.globals)
if len(virtualDirectory) > 0 {
if virtualDirectory[0] == '.' { // first check for .wrong
virtualDirectory = virtualDirectory[1:]
}
if virtualDirectory[0] == '/' || virtualDirectory[0] == os.PathSeparator { // second check for /something, (or ./something if we had dot on 0 it will be removed
virtualDirectory = virtualDirectory[1:]
}
}
s.mu.Lock()
defer s.mu.Unlock()
names := namesFn()
for _, path := range names {
if !strings.HasPrefix(path, virtualDirectory) {
continue
} }
rel, err := filepath.Rel(virtualDirectory, path) buf, err := asset(s.fs, path)
if err != nil { if err != nil {
return err return err
} }
ext := filepath.Ext(rel) name := strings.TrimPrefix(path, "/")
if ext == virtualExtension { s.templateCache[name], err = set.FromBytes(buf)
return err
buf, err := assetFn(path) })
if err != nil {
return err
}
name := filepath.ToSlash(rel)
s.templateCache[name], err = set.FromString(string(buf))
if err != nil {
return err
}
}
}
return nil
} }
// getPongoContext returns the pongo2.Context from map[string]interface{} or from pongo2.Context, used internaly // getPongoContext returns the pongo2.Context from map[string]interface{} or from pongo2.Context, used internaly
@ -350,15 +250,14 @@ func getPongoContext(templateData interface{}) pongo2.Context {
} }
func (s *DjangoEngine) fromCache(relativeName string) *pongo2.Template { func (s *DjangoEngine) fromCache(relativeName string) *pongo2.Template {
s.mu.Lock() if s.reload {
s.rmu.RLock()
defer s.rmu.RUnlock()
}
tmpl, ok := s.templateCache[relativeName] if tmpl, ok := s.templateCache[relativeName]; ok {
if ok {
s.mu.Unlock()
return tmpl return tmpl
} }
s.mu.Unlock()
return nil return nil
} }
@ -367,10 +266,6 @@ func (s *DjangoEngine) fromCache(relativeName string) *pongo2.Template {
func (s *DjangoEngine) ExecuteWriter(w io.Writer, filename string, layout string, bindingData interface{}) error { func (s *DjangoEngine) ExecuteWriter(w io.Writer, filename string, layout string, bindingData interface{}) error {
// re-parse the templates if reload is enabled. // re-parse the templates if reload is enabled.
if s.reload { if s.reload {
// locks to fix #872, it's the simplest solution and the most correct,
// to execute writers with "wait list", one at a time.
s.rmu.Lock()
defer s.rmu.Unlock()
if err := s.Load(); err != nil { if err := s.Load(); err != nil {
return err return err
} }
@ -380,5 +275,5 @@ func (s *DjangoEngine) ExecuteWriter(w io.Writer, filename string, layout string
return tmpl.ExecuteWriter(getPongoContext(bindingData), w) return tmpl.ExecuteWriter(getPongoContext(bindingData), w)
} }
return fmt.Errorf("template with name: %s ddoes not exist in the dir: %s", filename, s.directory) return ErrNotExist{filename, false}
} }

106
view/fs.go Normal file
View File

@ -0,0 +1,106 @@
package view
import (
"fmt"
"io/ioutil"
"net/http"
"path"
"path/filepath"
"sort"
)
// walk recursively in "fs" descends "root" path, calling "walkFn".
func walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error {
names, err := assetNames(fs, root)
if err != nil {
return fmt.Errorf("%s: %w", root, err)
}
for _, name := range names {
fullpath := path.Join(root, name)
f, err := fs.Open(fullpath)
if err != nil {
return fmt.Errorf("%s: %w", fullpath, err)
}
stat, err := f.Stat()
err = walkFn(fullpath, stat, err)
if err != nil {
if err != filepath.SkipDir {
return fmt.Errorf("%s: %w", fullpath, err)
}
continue
}
if stat.IsDir() {
if err := walk(fs, fullpath, walkFn); err != nil {
return fmt.Errorf("%s: %w", fullpath, err)
}
}
}
return nil
}
// assetNames returns the first-level directories and file, sorted, names.
func assetNames(fs http.FileSystem, name string) ([]string, error) {
f, err := fs.Open(name)
if err != nil {
return nil, err
}
infos, err := f.Readdir(-1)
f.Close()
if err != nil {
return nil, err
}
names := make([]string, 0, len(infos))
for _, info := range infos {
// note: go-bindata fs returns full names whether
// the http.Dir returns the base part, so
// we only work with their base names.
name := filepath.ToSlash(info.Name())
name = path.Base(name)
names = append(names, name)
}
sort.Strings(names)
return names, nil
}
func asset(fs http.FileSystem, name string) ([]byte, error) {
f, err := fs.Open(name)
if err != nil {
return nil, err
}
contents, err := ioutil.ReadAll(f)
f.Close()
return contents, err
}
func getFS(fsOrDir interface{}) (fs http.FileSystem) {
switch v := fsOrDir.(type) {
case string:
fs = httpDirWrapper{http.Dir(v)}
case http.FileSystem:
fs = v
default:
panic(fmt.Errorf(`unexpected "fsOrDir" argument type of %T (string or http.FileSystem)`, v))
}
return
}
// fixes: "invalid character in file path"
// on amber engine (it uses the virtual fs directly
// and it uses filepath instead of the path package...).
type httpDirWrapper struct {
http.Dir
}
func (d httpDirWrapper) Open(name string) (http.File, error) {
return d.Dir.Open(filepath.ToSlash(name))
}

View File

@ -3,7 +3,7 @@ package view
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -14,15 +14,16 @@ import (
// HandlebarsEngine contains the handlebars view engine structure. // HandlebarsEngine contains the handlebars view engine structure.
type HandlebarsEngine struct { type HandlebarsEngine struct {
fs http.FileSystem
// files configuration // files configuration
directory string rootDir string
extension string extension string
assetFn func(name string) ([]byte, error) // for embedded, in combination with directory & extension assetFn func(name string) ([]byte, error) // for embedded, in combination with directory & extension
namesFn func() []string // for embedded, in combination with directory & extension namesFn func() []string // for embedded, in combination with directory & extension
reload bool // if true, each time the ExecuteWriter is called the templates will be reloaded. reload bool // if true, each time the ExecuteWriter is called the templates will be reloaded.
// parser configuration // parser configuration
layout string layout string
rmu sync.RWMutex // locks for helpers and `ExecuteWiter` when `reload` is true. rmu sync.RWMutex
helpers map[string]interface{} helpers map[string]interface{}
templateCache map[string]*raymond.Template templateCache map[string]*raymond.Template
} }
@ -34,9 +35,15 @@ var (
// Handlebars creates and returns a new handlebars view engine. // Handlebars creates and returns a new handlebars view engine.
// The given "extension" MUST begin with a dot. // The given "extension" MUST begin with a dot.
func Handlebars(directory, extension string) *HandlebarsEngine { //
// Usage:
// Handlebars("./views", ".html") or
// Handlebars(iris.Dir("./views"), ".html") or
// Handlebars(AssetFile(), ".html") for embedded data.
func Handlebars(fs interface{}, extension string) *HandlebarsEngine {
s := &HandlebarsEngine{ s := &HandlebarsEngine{
directory: directory, fs: getFS(fs),
rootDir: "/",
extension: extension, extension: extension,
templateCache: make(map[string]*raymond.Template), templateCache: make(map[string]*raymond.Template),
helpers: make(map[string]interface{}), helpers: make(map[string]interface{}),
@ -54,20 +61,18 @@ func Handlebars(directory, extension string) *HandlebarsEngine {
return s return s
} }
// RootDir sets the directory to be used as a starting point
// to load templates from the provided file system.
func (s *HandlebarsEngine) RootDir(root string) *HandlebarsEngine {
s.rootDir = filepath.ToSlash(root)
return s
}
// Ext returns the file extension which this view engine is responsible to render. // Ext returns the file extension which this view engine is responsible to render.
func (s *HandlebarsEngine) Ext() string { func (s *HandlebarsEngine) Ext() string {
return s.extension return s.extension
} }
// Binary optionally, use it when template files are distributed
// inside the app executable (.go generated files).
//
// The assetFn and namesFn can come from the go-bindata library.
func (s *HandlebarsEngine) Binary(assetFn func(name string) ([]byte, error), namesFn func() []string) *HandlebarsEngine {
s.assetFn, s.namesFn = assetFn, namesFn
return s
}
// Reload if set to true the templates are reloading on each render, // 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 // use it when you're in development and you're boring of restarting
// the whole app when you edit a template file. // the whole app when you edit a template file.
@ -105,135 +110,52 @@ func (s *HandlebarsEngine) AddFunc(funcName string, funcBody interface{}) {
// //
// Returns an error if something bad happens, user is responsible to catch it. // Returns an error if something bad happens, user is responsible to catch it.
func (s *HandlebarsEngine) Load() error { func (s *HandlebarsEngine) Load() error {
if s.assetFn != nil && s.namesFn != nil { s.rmu.Lock()
// embedded defer s.rmu.Unlock()
return s.loadAssets()
}
// load from directory, make the dir absolute here too.
dir, err := filepath.Abs(s.directory)
if err != nil {
return err
}
if _, err := os.Stat(dir); os.IsNotExist(err) {
return err
}
// change the directory field configuration, load happens after directory has been set, so we will not have any problems here.
s.directory = dir
return s.loadDirectory()
}
// loadDirectory builds the handlebars templates from directory.
func (s *HandlebarsEngine) loadDirectory() error {
// register the global helpers on the first load // register the global helpers on the first load
if len(s.templateCache) == 0 && s.helpers != nil { if len(s.templateCache) == 0 && s.helpers != nil {
raymond.RegisterHelpers(s.helpers) raymond.RegisterHelpers(s.helpers)
} }
dir, extension := s.directory, s.extension return walk(s.fs, s.rootDir, func(path string, info os.FileInfo, _ error) error {
// the render works like {{ render "myfile.html" theContext.PartialContext}}
// instead of the html/template engine which works like {{ render "myfile.html"}} and accepts the parent binding, with handlebars we can't do that because of lack of runtime helpers (dublicate error)
var templateErr error
err := filepath.Walk(dir, func(path string, info os.FileInfo, _ error) error {
if info == nil || info.IsDir() { if info == nil || info.IsDir() {
return nil return nil
} }
rel, err := filepath.Rel(dir, path) if s.extension != "" {
if !strings.HasSuffix(path, s.extension) {
return nil
}
}
buf, err := asset(s.fs, path)
if err != nil { if err != nil {
return err return err
} }
ext := filepath.Ext(rel) name := strings.TrimPrefix(path, "/")
if ext == extension { tmpl, err := raymond.Parse(string(buf))
buf, err := ioutil.ReadFile(path) if err != nil {
contents := string(buf) return err
if err != nil {
templateErr = err
return err
}
name := filepath.ToSlash(rel)
tmpl, err := raymond.Parse(contents)
if err != nil {
templateErr = err
return err
}
s.templateCache[name] = tmpl
} }
s.templateCache[name] = tmpl
return nil return nil
}) })
if err != nil {
return err
}
return templateErr
}
// loadAssets loads the templates by binary, embedded.
func (s *HandlebarsEngine) loadAssets() error {
// register the global helpers
if len(s.templateCache) == 0 && s.helpers != nil {
raymond.RegisterHelpers(s.helpers)
}
virtualDirectory, virtualExtension := s.directory, s.extension
assetFn, namesFn := s.assetFn, s.namesFn
if len(virtualDirectory) > 0 {
if virtualDirectory[0] == '.' { // first check for .wrong
virtualDirectory = virtualDirectory[1:]
}
if virtualDirectory[0] == '/' || virtualDirectory[0] == os.PathSeparator { // second check for /something, (or ./something if we had dot on 0 it will be removed
virtualDirectory = virtualDirectory[1:]
}
}
names := namesFn()
for _, path := range names {
if !strings.HasPrefix(path, virtualDirectory) {
continue
}
ext := filepath.Ext(path)
if ext == virtualExtension {
rel, err := filepath.Rel(virtualDirectory, path)
if err != nil {
return err
}
buf, err := assetFn(path)
if err != nil {
return err
}
contents := string(buf)
name := filepath.ToSlash(rel)
tmpl, err := raymond.Parse(contents)
if err != nil {
return err
}
s.templateCache[name] = tmpl
}
}
return nil
} }
func (s *HandlebarsEngine) fromCache(relativeName string) *raymond.Template { func (s *HandlebarsEngine) fromCache(relativeName string) *raymond.Template {
tmpl, ok := s.templateCache[relativeName] if s.reload {
if !ok { s.rmu.RLock()
return nil defer s.rmu.RUnlock()
} }
return tmpl
if tmpl, ok := s.templateCache[relativeName]; ok {
return tmpl
}
return nil
} }
func (s *HandlebarsEngine) executeTemplateBuf(name string, binding interface{}) (string, error) { func (s *HandlebarsEngine) executeTemplateBuf(name string, binding interface{}) (string, error) {
@ -247,10 +169,6 @@ func (s *HandlebarsEngine) executeTemplateBuf(name string, binding interface{})
func (s *HandlebarsEngine) ExecuteWriter(w io.Writer, filename string, layout string, bindingData interface{}) error { func (s *HandlebarsEngine) ExecuteWriter(w io.Writer, filename string, layout string, bindingData interface{}) error {
// re-parse the templates if reload is enabled. // re-parse the templates if reload is enabled.
if s.reload { if s.reload {
// locks to fix #872, it's the simplest solution and the most correct,
// to execute writers with "wait list", one at a time.
s.rmu.Lock()
defer s.rmu.Unlock()
if err := s.Load(); err != nil { if err := s.Load(); err != nil {
return err return err
} }
@ -295,5 +213,5 @@ func (s *HandlebarsEngine) ExecuteWriter(w io.Writer, filename string, layout st
return err return err
} }
return fmt.Errorf("template with name: %s[original name = %s] does not exist in the dir: %s", renderFilename, filename, s.directory) return ErrNotExist{fmt.Sprintf("%s (file: %s)", renderFilename, filename), false}
} }

View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"html/template" "html/template"
"io" "io"
"io/ioutil" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -14,14 +14,16 @@ import (
// HTMLEngine contains the html view engine structure. // HTMLEngine contains the html view engine structure.
type HTMLEngine struct { type HTMLEngine struct {
// the file system to load from.
fs http.FileSystem
// files configuration // files configuration
directory string rootDir string
extension string extension string
assetFn func(name string) ([]byte, error) // for embedded, in combination with directory & extension // if true, each time the ExecuteWriter is called the templates will be reloaded,
namesFn func() []string // for embedded, in combination with directory & extension // each ExecuteWriter waits to be finished before writing to a new one.
reload bool // if true, each time the ExecuteWriter is called the templates will be reloaded, each ExecuteWriter waits to be finished before writing to a new one. reload bool
// parser configuration // parser configuration
options []string // text options options []string // (text) template options
left string left string
right string right string
layout string layout string
@ -65,12 +67,16 @@ var emptyFuncs = template.FuncMap{
// The html engine used like the "html/template" standard go package // The html engine used like the "html/template" standard go package
// but with a lot of extra features. // but with a lot of extra features.
// The given "extension" MUST begin with a dot. // The given "extension" MUST begin with a dot.
func HTML(directory, extension string) *HTMLEngine { //
// Usage:
// HTML("./views", ".html") or
// HTML(iris.Dir("./views"), ".html") or
// HTML(AssetFile(), ".html") for embedded data.
func HTML(fs interface{}, extension string) *HTMLEngine {
s := &HTMLEngine{ s := &HTMLEngine{
directory: directory, fs: getFS(fs),
rootDir: "/",
extension: extension, extension: extension,
assetFn: nil,
namesFn: nil,
reload: false, reload: false,
left: "{{", left: "{{",
right: "}}", right: "}}",
@ -82,20 +88,18 @@ func HTML(directory, extension string) *HTMLEngine {
return s return s
} }
// RootDir sets the directory to be used as a starting point
// to load templates from the provided file system.
func (s *HTMLEngine) RootDir(root string) *HTMLEngine {
s.rootDir = filepath.ToSlash(root)
return s
}
// Ext returns the file extension which this view engine is responsible to render. // Ext returns the file extension which this view engine is responsible to render.
func (s *HTMLEngine) Ext() string { func (s *HTMLEngine) Ext() string {
return s.extension return s.extension
} }
// Binary optionally, use it when template files are distributed
// inside the app executable (.go generated files).
//
// The assetFn and namesFn can come from the go-bindata library.
func (s *HTMLEngine) Binary(assetFn func(name string) ([]byte, error), namesFn func() []string) *HTMLEngine {
s.assetFn, s.namesFn = assetFn, namesFn
return s
}
// Reload if set to true the templates are reloading on each render, // 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 // use it when you're in development and you're boring of restarting
// the whole app when you edit a template file. // the whole app when you edit a template file.
@ -211,213 +215,49 @@ func (s *HTMLEngine) Funcs(funcMap template.FuncMap) *HTMLEngine {
// //
// Returns an error if something bad happens, caller is responsible to handle that. // Returns an error if something bad happens, caller is responsible to handle that.
func (s *HTMLEngine) Load() error { func (s *HTMLEngine) Load() error {
// No need to make this with a complicated and "pro" way, just add lockers to the `ExecuteWriter`. s.rmu.Lock()
// if `Reload(true)` and add a note for non conc access on dev mode. defer s.rmu.Unlock()
// atomic.StoreUint32(&s.isLoading, 1)
// s.rmu.Lock()
// defer func() {
// s.rmu.Unlock()
// atomic.StoreUint32(&s.isLoading, 0)
// }()
if s.assetFn != nil && s.namesFn != nil { s.Templates = template.New(s.rootDir)
// NOT NECESSARY "fix" of https://github.com/kataras/iris/issues/784,
// IT'S BAD CODE WRITTEN WE KEEP HERE ONLY FOR A REMINDER
// for any future questions.
//
// if strings.HasPrefix(s.directory, "../") {
// // this and some more additions are fixes for https://github.com/kataras/iris/issues/784
// // however, the dev SHOULD
// // run the go-bindata command from the "$dir" parent directory
// // and just use the ./$dir in the declaration,
// // so all these fixes are not really necessary but they are here
// // for the future
// dir, err := filepath.Abs(s.directory)
// // the absolute dir here can be invalid if running from other
// // folder but we really don't care
// // when we're working with the bindata because
// // we only care for its relative directory
// // see `loadAssets` for more.
// if err != nil {
// return err
// }
// s.directory = dir
// }
// embedded
return s.loadAssets()
}
// load from directory, make the dir absolute here too.
dir, err := filepath.Abs(s.directory)
if err != nil {
return err
}
if _, err := os.Stat(dir); os.IsNotExist(err) {
return err
}
// change the directory field configuration, load happens after directory has been set, so we will not have any problems here.
s.directory = dir
return s.loadDirectory()
}
// loadDirectory builds the templates from directory.
func (s *HTMLEngine) loadDirectory() error {
dir, extension := s.directory, s.extension
var templateErr error
s.Templates = template.New(dir)
s.Templates.Delims(s.left, s.right) s.Templates.Delims(s.left, s.right)
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { return walk(s.fs, s.rootDir, func(path string, info os.FileInfo, err error) error {
if info == nil || info.IsDir() { if info == nil || info.IsDir() {
} else { return nil
rel, err := filepath.Rel(dir, path) }
if s.extension != "" {
if !strings.HasSuffix(path, s.extension) {
return nil
}
}
buf, err := asset(s.fs, path)
if err != nil {
return fmt.Errorf("%s: %w", path, err)
}
contents := string(buf)
name := strings.TrimPrefix(path, "/")
tmpl := s.Templates.New(name)
tmpl.Option(s.options...)
if s.middleware != nil {
contents, err = s.middleware(name, buf)
if err != nil { if err != nil {
templateErr = err
return err return err
} }
ext := filepath.Ext(path)
if ext == extension {
buf, err := ioutil.ReadFile(path)
if err != nil {
templateErr = err
return err
}
contents := string(buf)
name := filepath.ToSlash(rel)
tmpl := s.Templates.New(name)
tmpl.Option(s.options...)
if s.middleware != nil {
contents, err = s.middleware(name, buf)
}
if err != nil {
templateErr = err
return err
}
// s.mu.Lock()
// Add our funcmaps.
_, err = tmpl.
Funcs(emptyFuncs).
// Funcs(s.makeDefaultLayoutFuncs(name)).
// Funcs(s.layoutFuncs).
Funcs(s.funcs).
Parse(contents)
// s.mu.Unlock()
if err != nil {
templateErr = err
return err
}
}
} }
return nil
})
if err != nil { // Add our funcmaps.
_, err = tmpl.
Funcs(emptyFuncs).
// Funcs(s.makeDefaultLayoutFuncs(name)).
// Funcs(s.layoutFuncs).
Funcs(s.funcs).
Parse(contents)
return err return err
} })
return templateErr
}
// loadAssets loads the templates by binary (go-bindata for embedded).
func (s *HTMLEngine) loadAssets() error {
virtualDirectory, virtualExtension := s.directory, s.extension
assetFn, namesFn := s.assetFn, s.namesFn
var templateErr error
s.Templates = template.New(virtualDirectory)
s.Templates.Delims(s.left, s.right)
names := namesFn()
if len(virtualDirectory) > 0 {
if virtualDirectory[0] == '.' { // first check for .wrong
virtualDirectory = virtualDirectory[1:]
}
if virtualDirectory[0] == '/' || virtualDirectory[0] == os.PathSeparator { // second check for /something, (or ./something if we had dot on 0 it will be removed
virtualDirectory = virtualDirectory[1:]
}
}
for _, path := range names {
// if filepath.IsAbs(virtualDirectory) {
// // fixes https://github.com/kataras/iris/issues/784
// // we take the absolute fullpath of the template file.
// pathFileAbs, err := filepath.Abs(path)
// if err != nil {
// templateErr = err
// continue
// }
//
// path = pathFileAbs
// }
// bindata may contain more files than the templates
// so keep that check as it's.
if !strings.HasPrefix(path, virtualDirectory) {
continue
}
ext := filepath.Ext(path)
// check if extension matches
if ext == virtualExtension {
// take the relative path of the path as base of
// virtualDirectory (the absolute path of the view engine that dev passed).
rel, err := filepath.Rel(virtualDirectory, path)
if err != nil {
templateErr = err
break
}
// // take the current working directory
// cpath, err := filepath.Abs(".")
// if err == nil {
// // set the path as relative to "path" of the current working dir.
// // fixes https://github.com/kataras/iris/issues/784
// rpath, err := filepath.Rel(cpath, path)
// // fix view: Asset not found for path ''
// if err == nil && rpath != "" {
// path = rpath
// }
// }
buf, err := assetFn(path)
if err != nil {
templateErr = fmt.Errorf("%v for path '%s'", err, path)
break
}
contents := string(buf)
name := filepath.ToSlash(rel)
// name should be the filename of the template.
tmpl := s.Templates.New(name)
tmpl.Option(s.options...)
if s.middleware != nil {
contents, err = s.middleware(name, buf)
if err != nil {
templateErr = fmt.Errorf("%v for name '%s'", err, name)
break
}
}
// Add our funcmaps.
if _, err = tmpl.Funcs(emptyFuncs).Funcs(s.funcs).Parse(contents); err != nil {
templateErr = err
break
}
}
}
return templateErr
} }
func (s *HTMLEngine) executeTemplateBuf(name string, binding interface{}) (*bytes.Buffer, error) { func (s *HTMLEngine) executeTemplateBuf(name string, binding interface{}) (*bytes.Buffer, error) {
@ -494,10 +334,6 @@ func (s *HTMLEngine) runtimeFuncsFor(t *template.Template, name string, binding
func (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bindingData interface{}) error { func (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bindingData interface{}) error {
// re-parse the templates if reload is enabled. // re-parse the templates if reload is enabled.
if s.reload { if s.reload {
// locks to fix #872, it's the simplest solution and the most correct,
// to execute writers with "wait list", one at a time.
s.rmu.Lock()
defer s.rmu.Unlock()
if err := s.Load(); err != nil { if err := s.Load(); err != nil {
return err return err
} }
@ -505,14 +341,14 @@ func (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bind
t := s.Templates.Lookup(name) t := s.Templates.Lookup(name)
if t == nil { if t == nil {
return fmt.Errorf("template: %s does not exist in the dir: %s", name, s.directory) return ErrNotExist{name, false}
} }
s.runtimeFuncsFor(t, name, bindingData) s.runtimeFuncsFor(t, name, bindingData)
if layout = getLayout(layout, s.layout); layout != "" { if layout = getLayout(layout, s.layout); layout != "" {
lt := s.Templates.Lookup(layout) lt := s.Templates.Lookup(layout)
if lt == nil { if lt == nil {
return fmt.Errorf("layout: %s does not exist in the dir: %s", name, s.directory) return ErrNotExist{name, true}
} }
s.layoutFuncsFor(lt, name, bindingData) s.layoutFuncsFor(lt, name, bindingData)

View File

@ -3,8 +3,7 @@ package view
import ( import (
"fmt" "fmt"
"io" "io"
"os" "net/http"
"path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings" "strings"
@ -18,9 +17,10 @@ const jetEngineName = "jet"
// JetEngine is the jet template parser's view engine. // JetEngine is the jet template parser's view engine.
type JetEngine struct { type JetEngine struct {
directory string fs http.FileSystem
rootDir string
extension string extension string
// physical system files or app-embedded, see `Binary(..., ...)`. Defaults to file system on initialization.
loader jet.Loader loader jet.Loader
developmentMode bool developmentMode bool
@ -51,11 +51,12 @@ var jetExtensions = [...]string{
// Jet creates and returns a new jet view engine. // Jet creates and returns a new jet view engine.
// The given "extension" MUST begin with a dot. // The given "extension" MUST begin with a dot.
func Jet(directory, extension string) *JetEngine { //
// if _, err := os.Stat(directory); os.IsNotExist(err) { // Usage:
// panic(err) // Jet("./views", ".jet") or
// } // Jet(iris.Dir("./views"), ".jet") or
// Jet(AssetFile(), ".jet") for embedded data.
func Jet(fs interface{}, extension string) *JetEngine {
extOK := false extOK := false
for _, ext := range jetExtensions { for _, ext := range jetExtensions {
if ext == extension { if ext == extension {
@ -69,9 +70,9 @@ func Jet(directory, extension string) *JetEngine {
} }
s := &JetEngine{ s := &JetEngine{
directory: directory, rootDir: "/",
extension: extension, extension: extension,
loader: jet.NewOSFileSystemLoader(directory), loader: &jetLoader{fs: getFS(fs)},
jetDataContextKey: "_jet", jetDataContextKey: "_jet",
} }
@ -83,6 +84,13 @@ func (s *JetEngine) String() string {
return jetEngineName return jetEngineName
} }
// RootDir sets the directory to be used as a starting point
// to load templates from the provided file system.
func (s *JetEngine) RootDir(root string) *JetEngine {
s.rootDir = filepath.ToSlash(root)
return s
}
// Ext should return the final file extension which this view engine is responsible to render. // Ext should return the final file extension which this view engine is responsible to render.
func (s *JetEngine) Ext() string { func (s *JetEngine) Ext() string {
return s.extension return s.extension
@ -175,121 +183,27 @@ func (s *JetEngine) Reload(developmentMode bool) *JetEngine {
} }
// SetLoader can be used when the caller wants to use something like // SetLoader can be used when the caller wants to use something like
// multi.Loader or httpfs.Loader of the jet subpackages, // multi.Loader or httpfs.Loader.
// overrides any previous loader may set by `Binary` or the default.
// Should act before `Load` or `iris.Application#RegisterView`.
func (s *JetEngine) SetLoader(loader jet.Loader) *JetEngine { func (s *JetEngine) SetLoader(loader jet.Loader) *JetEngine {
s.loader = loader s.loader = loader
return s return s
} }
// Binary optionally, use it when template files are distributed type jetLoader struct {
// inside the app executable (.go generated files). fs http.FileSystem
//
// The assetFn and namesFn can come from the go-bindata library.
// Should act before `Load` or `iris.Application#RegisterView`.
func (s *JetEngine) Binary(assetFn func(name string) ([]byte, error), assetNames func() []string) *JetEngine {
// embedded.
vdir := s.directory
if vdir[0] == '.' {
vdir = vdir[1:]
}
// second check for /something, (or ./something if we had dot on 0 it will be removed)
if vdir[0] == '/' || vdir[0] == os.PathSeparator {
vdir = vdir[1:]
}
// check for trailing slashes because new users may be do that by mistake
// although all examples are showing the correct way but you never know
// i.e "./assets/" is not correct, if was inside "./assets".
// remove last "/".
if trailingSlashIdx := len(vdir) - 1; vdir[trailingSlashIdx] == '/' {
vdir = vdir[0:trailingSlashIdx]
}
namesSlice := assetNames()
names := make(map[string]struct{})
for _, name := range namesSlice {
if !strings.HasPrefix(name, vdir) {
continue
}
extOK := false
fileExt := path.Ext(name)
for _, ext := range jetExtensions {
if ext == fileExt {
extOK = true
break
}
}
if !extOK {
continue
}
names[name] = struct{}{}
}
if len(names) == 0 {
panic("JetEngine.Binary: no embedded files found in directory: " + vdir)
}
s.loader = &embeddedLoader{
vdir: vdir,
asset: assetFn,
names: names,
}
return s
} }
type ( var _ jet.Loader = (*jetLoader)(nil)
embeddedLoader struct {
vdir string
asset func(name string) ([]byte, error)
names map[string]struct{}
}
embeddedFile struct {
contents []byte // the contents are NOT consumed.
readen int64
}
)
var ( // Open opens a file from file system.
_ jet.Loader = (*embeddedLoader)(nil) func (l *jetLoader) Open(name string) (io.ReadCloser, error) {
_ io.ReadCloser = (*embeddedFile)(nil) return l.fs.Open(name)
)
func (f *embeddedFile) Close() error { return nil }
func (f *embeddedFile) Read(p []byte) (int, error) {
if f.readen >= int64(len(f.contents)) {
return 0, io.EOF
}
n := copy(p, f.contents[f.readen:])
f.readen += int64(n)
return n, nil
}
// Open opens a file from OS file system.
func (l *embeddedLoader) Open(name string) (io.ReadCloser, error) {
name = path.Join(l.vdir, filepath.ToSlash(name))
contents, err := l.asset(name)
if err != nil {
return nil, err
}
return &embeddedFile{
contents: contents,
}, nil
} }
// Exists checks if the template name exists by walking the list of template paths // Exists checks if the template name exists by walking the list of template paths
// returns string with the full path of the template and bool true if the template file was found // returns string with the full path of the template and bool true if the template file was found
func (l *embeddedLoader) Exists(name string) (string, bool) { func (l *jetLoader) Exists(name string) (string, bool) {
name = path.Join(l.vdir, filepath.ToSlash(name)) if _, err := l.fs.Open(name); err == nil {
if _, ok := l.names[name]; ok {
return name, true return name, true
} }

View File

@ -2,9 +2,6 @@ package view
import ( import (
"bytes" "bytes"
"io/ioutil"
"path"
"strings"
"github.com/iris-contrib/jade" "github.com/iris-contrib/jade"
) )
@ -16,26 +13,23 @@ import (
// //
// Read more about the Jade Go Parser: https://github.com/Joker/jade // Read more about the Jade Go Parser: https://github.com/Joker/jade
// //
// Usage:
// Pug("./views", ".pug") or
// Pug(iris.Dir("./views"), ".pug") or
// Pug(AssetFile(), ".pug") for embedded data.
//
// Examples: // Examples:
// https://github.com/kataras/iris/tree/master/_examples/view/template_pug_0 // https://github.com/kataras/iris/tree/master/_examples/view/template_pug_0
// https://github.com/kataras/iris/tree/master/_examples/view/template_pug_1 // https://github.com/kataras/iris/tree/master/_examples/view/template_pug_1
// https://github.com/kataras/iris/tree/master/_examples/view/template_pug_2 // https://github.com/kataras/iris/tree/master/_examples/view/template_pug_2
// https://github.com/kataras/iris/tree/master/_examples/view/template_pug_3 // https://github.com/kataras/iris/tree/master/_examples/view/template_pug_3
func Pug(directory, extension string) *HTMLEngine { func Pug(fs interface{}, extension string) *HTMLEngine {
s := HTML(directory, extension) s := HTML(fs, extension)
s.middleware = func(name string, text []byte) (contents string, err error) { s.middleware = func(name string, text []byte) (contents string, err error) {
name = path.Join(path.Clean(directory), name)
tmpl := jade.New(name) tmpl := jade.New(name)
tmpl.ReadFunc = func(name string) ([]byte, error) { tmpl.ReadFunc = func(name string) ([]byte, error) {
if !strings.HasPrefix(path.Clean(name), path.Clean(directory)) { return asset(s.fs, name)
name = path.Join(directory, name)
}
if s.assetFn != nil {
return s.assetFn(name)
}
return ioutil.ReadFile(name)
} }
// Fixes: https://github.com/kataras/iris/issues/1450 // Fixes: https://github.com/kataras/iris/issues/1450
@ -43,7 +37,6 @@ func Pug(directory, extension string) *HTMLEngine {
// And Also able to use relative paths on "extends" and "include" directives: // And Also able to use relative paths on "extends" and "include" directives:
// e.g. instead of extends "templates/layout.pug" we use "layout.pug" // e.g. instead of extends "templates/layout.pug" we use "layout.pug"
// so contents of templates are independent of their root location. // so contents of templates are independent of their root location.
exec, err := tmpl.Parse(text) exec, err := tmpl.Parse(text)
if err != nil { if err != nil {
return return

View File

@ -19,6 +19,21 @@ type (
EngineFuncer = context.ViewEngineFuncer EngineFuncer = context.ViewEngineFuncer
) )
// ErrNotExist reports whether a template was not found in the parsed templates tree.
type ErrNotExist struct {
Name string
IsLayout bool
}
// Error implements the `error` interface.
func (e ErrNotExist) Error() string {
title := "template"
if e.IsLayout {
title = "layout"
}
return fmt.Sprintf("%s '%s' does not exist", title, e.Name)
}
// View is responsible to // View is responsible to
// load the correct templates // load the correct templates
// for each of the registered view engines. // for each of the registered view engines.