From e1f25eb098b9d05b211657a37d188fc2f791f94e Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sat, 5 Sep 2020 08:34:09 +0300 Subject: [PATCH] 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) --- HISTORY.md | 5 +- _examples/README.md | 11 +- .../embedding-files-into-app/main.go | 4 +- .../embedding-gzipped-files-into-app/main.go | 4 +- .../file-server/http2push-embedded/main.go | 4 +- .../main.go | 4 +- .../bindata.go | 84 ++--- .../embedded-single-page-application/main.go | 18 +- .../embedding-templates-into-app/bindata.go | 178 ++++++--- .../view/embedding-templates-into-app/main.go | 23 +- .../template_ace_0/views/layouts/main.ace | 1 + _examples/view/template_amber_0/main.go | 20 + .../view/template_amber_0/views/index.amber | 11 + .../template_amber_0/views/layouts/main.amber | 15 + .../view/template_amber_1_embedded/bindata.go | 357 ++++++++++++++++++ .../view/template_amber_1_embedded/main.go | 30 ++ .../template_blocks_1_embedded/bindata.go | 205 +++++++--- .../view/template_blocks_1_embedded/main.go | 16 +- .../view/template_jet_1_embedded/README.md | 25 -- .../view/template_jet_1_embedded/bindata.go | 207 +++++++--- .../view/template_jet_1_embedded/main.go | 22 +- _examples/view/template_jet_3/main.go | 6 +- _examples/view/template_jet_3/views/index.jet | 15 +- _examples/view/template_pug_0/main.go | 34 +- .../view/template_pug_0/templates/index.pug | 41 +- _examples/view/template_pug_1/main.go | 34 +- .../templates/footer.pug | 0 .../templates/header.pug | 0 .../view/template_pug_1/templates/index.pug | 25 +- _examples/view/template_pug_2/main.go | 28 -- .../view/template_pug_2/templates/index.pug | 7 - .../bindata.go | 154 ++++++-- .../view/template_pug_2_embedded/main.go | 28 ++ .../templates/index.pug | 0 .../templates/layout.pug | 0 _examples/view/template_pug_3/main.go | 23 -- core/router/api_builder.go | 19 +- core/router/party.go | 11 +- go.mod | 2 +- view/README.md | 23 +- view/ace.go | 12 +- view/amber.go | 164 +++----- view/blocks.go | 36 +- view/django.go | 195 +++------- view/fs.go | 106 ++++++ view/handlebars.go | 172 +++------ view/html.go | 280 +++----------- view/jet.go | 142 ++----- view/pug.go | 23 +- view/view.go | 15 + 50 files changed, 1613 insertions(+), 1226 deletions(-) create mode 100644 _examples/view/template_amber_0/main.go create mode 100644 _examples/view/template_amber_0/views/index.amber create mode 100644 _examples/view/template_amber_0/views/layouts/main.amber create mode 100644 _examples/view/template_amber_1_embedded/bindata.go create mode 100644 _examples/view/template_amber_1_embedded/main.go delete mode 100644 _examples/view/template_jet_1_embedded/README.md rename _examples/view/{template_pug_2 => template_pug_1}/templates/footer.pug (100%) rename _examples/view/{template_pug_2 => template_pug_1}/templates/header.pug (100%) delete mode 100644 _examples/view/template_pug_2/main.go delete mode 100644 _examples/view/template_pug_2/templates/index.pug rename _examples/view/{template_pug_3 => template_pug_2_embedded}/bindata.go (51%) create mode 100644 _examples/view/template_pug_2_embedded/main.go rename _examples/view/{template_pug_3 => template_pug_2_embedded}/templates/index.pug (100%) rename _examples/view/{template_pug_3 => template_pug_2_embedded}/templates/layout.pug (100%) delete mode 100644 _examples/view/template_pug_3/main.go create mode 100644 view/fs.go diff --git a/HISTORY.md b/HISTORY.md index af1eb739..350bb5b5 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -364,6 +364,8 @@ Response: 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. - Improved tracing (with `app.Logger().SetLevel("debug")`) for routes. Screens: @@ -641,6 +643,7 @@ New Context Methods: 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. - `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). @@ -652,7 +655,7 @@ Breaking Changes: - `iris.Gzip` and `iris.GzipReader` replaced with `iris.Compression` (middleware). - `ctx.ClientSupportsGzip() bool` replaced with `ctx.ClientSupportsEncoding("gzip", "br" ...) bool`. - `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.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). diff --git a/_examples/README.md b/_examples/README.md index 44d24cac..bf12aebe 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -113,15 +113,16 @@ * [Write to a custom `io.Writer`](view/write-to) * [Blocks](view/template_blocks_0) * [Blocks Embedded](view/template_blocks_1_embedded) - * [Pug: Greeting](view/template_pug_0) - * [Pug: `Actions`](view/template_pug_1) - * [Pug: `Includes`](view/template_pug_2) - * [Pug: `Extends`](view/template_pug_3) - * [Jet Template](view/template_jet_0) + * [Pug: `Actions`](view/template_pug_0) + * [Pug: `Includes`](view/template_pug_1) + * [Pug Embedded`](view/template_pug_2_embedded) + * [Jet](view/template_jet_0) * [Jet Embedded](view/template_jet_1_embedded) * [Jet 'urlpath' tmpl func](view/template_jet_2) * [Jet Template Funcs from Struct](view/template_jet_3) * [Ace](view/template_ace_0) + * [Amber](view/template_amber_0) + * [Amber Embedded](view/template_amber_1_embedded) * [Handlebars](view/template_handlebars_0) * Third-Parties * [Render `valyala/quicktemplate` templates](view/quicktemplate) diff --git a/_examples/file-server/embedding-files-into-app/main.go b/_examples/file-server/embedding-files-into-app/main.go index 344f2fe6..2112faac 100644 --- a/_examples/file-server/embedding-files-into-app/main.go +++ b/_examples/file-server/embedding-files-into-app/main.go @@ -5,7 +5,9 @@ import ( ) // 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 run . // "physical" files are not used, you can delete the "assets" folder and run the example. diff --git a/_examples/file-server/embedding-gzipped-files-into-app/main.go b/_examples/file-server/embedding-gzipped-files-into-app/main.go index a80814c2..edc2c418 100644 --- a/_examples/file-server/embedding-gzipped-files-into-app/main.go +++ b/_examples/file-server/embedding-gzipped-files-into-app/main.go @@ -6,7 +6,9 @@ import ( // 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 run . // Time to complete the compression and caching of [2/3] files: 31.9998ms diff --git a/_examples/file-server/http2push-embedded/main.go b/_examples/file-server/http2push-embedded/main.go index a29283f2..3dce2d7d 100644 --- a/_examples/file-server/http2push-embedded/main.go +++ b/_examples/file-server/http2push-embedded/main.go @@ -7,7 +7,9 @@ import ( ) // 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/... // # OR if the ./assets directory was inside this example foder: // # go-bindata -nomemcopy -refix "assets" ./assets/... diff --git a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go b/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go index 5e716f02..8629d026 100644 --- a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go +++ b/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go @@ -2,7 +2,9 @@ package main 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 run . diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go b/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go index c9cf99a6..8ab6df96 100644 --- a/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go +++ b/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go @@ -20,8 +20,8 @@ import ( "time" ) -func bindataRead(data, name string) ([]byte, error) { - gz, err := gzip.NewReader(strings.NewReader(data)) +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) } @@ -168,82 +168,82 @@ func AssetFile() http.FileSystem { 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( - _publicAppJs, - "public/app.js", + _appJs, + "app.js", ) } -func publicAppJs() (*asset, error) { - bytes, err := publicAppJsBytes() +func appJs() (*asset, error) { + bytes, err := appJsBytes() if err != nil { 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} 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( - _publicApp2IndexHtml, - "public/app2/index.html", + _app2IndexHtml, + "app2/index.html", ) } -func publicApp2IndexHtml() (*asset, error) { - bytes, err := publicApp2IndexHtmlBytes() +func app2IndexHtml() (*asset, error) { + bytes, err := app2IndexHtmlBytes() if err != nil { 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} 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( - _publicCssMainCss, - "public/css/main.css", + _cssMainCss, + "css/main.css", ) } -func publicCssMainCss() (*asset, error) { - bytes, err := publicCssMainCssBytes() +func cssMainCss() (*asset, error) { + bytes, err := cssMainCssBytes() if err != nil { 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} 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( - _publicIndexHtml, - "public/index.html", + _indexHtml, + "index.html", ) } -func publicIndexHtml() (*asset, error) { - bytes, err := publicIndexHtmlBytes() +func indexHtml() (*asset, error) { + bytes, err := indexHtmlBytes() if err != nil { 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} return a, nil } @@ -300,10 +300,10 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "public/app.js": publicAppJs, - "public/app2/index.html": publicApp2IndexHtml, - "public/css/main.css": publicCssMainCss, - "public/index.html": publicIndexHtml, + "app.js": appJs, + "app2/index.html": app2IndexHtml, + "css/main.css": cssMainCss, + "index.html": indexHtml, } // AssetDir returns the file names below a certain @@ -347,16 +347,14 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "public": {nil, map[string]*bintree{ - "app.js": {publicAppJs, map[string]*bintree{}}, - "app2": {nil, 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{}}, + "app.js": {appJs, map[string]*bintree{}}, + "app2": {nil, map[string]*bintree{ + "index.html": {app2IndexHtml, 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 diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/main.go b/_examples/file-server/single-page-application/embedded-single-page-application/main.go index f6ff0055..24f296da 100644 --- a/_examples/file-server/single-page-application/embedded-single-page-application/main.go +++ b/_examples/file-server/single-page-application/embedded-single-page-application/main.go @@ -4,8 +4,10 @@ import ( "github.com/kataras/iris/v12" ) -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata -nomemcopy -fs ./public/... +// $ 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 run . var page = struct { @@ -14,20 +16,16 @@ var page = struct { func newApp() *iris.Application { 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) { ctx.ViewData("Page", page) 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 } diff --git a/_examples/view/embedding-templates-into-app/bindata.go b/_examples/view/embedding-templates-into-app/bindata.go index e2a2821b..4075f21a 100644 --- a/_examples/view/embedding-templates-into-app/bindata.go +++ b/_examples/view/embedding-templates-into-app/bindata.go @@ -1,5 +1,6 @@ -// Code generated for package main by go-bindata DO NOT EDIT. (@generated) -// sources: +// Code generated by go-bindata. (@generated) DO NOT EDIT. + +//Package main generated by go-bindata.// sources: // templates/layouts/layout.html // templates/layouts/mylayout.html // templates/page1.html @@ -12,6 +13,7 @@ import ( "fmt" "io" "io/ioutil" + "net/http" "os" "path/filepath" "strings" @@ -21,7 +23,7 @@ import ( 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) + return nil, fmt.Errorf("read %q: %v", name, err) } var buf bytes.Buffer @@ -29,7 +31,7 @@ func bindataRead(data []byte, name string) ([]byte, error) { clErr := gz.Close() if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) + return nil, fmt.Errorf("read %q: %v", name, err) } if clErr != nil { return nil, err @@ -65,7 +67,7 @@ func (fi bindataFileInfo) Mode() os.FileMode { return fi.mode } -// Mode return file modify time +// ModTime return file modify time func (fi bindataFileInfo) ModTime() time.Time { return fi.modTime } @@ -80,82 +82,168 @@ func (fi bindataFileInfo) Sys() interface{} { 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( - _templatesLayoutsLayoutHtml, - "templates/layouts/layout.html", + _layoutsLayoutHtml, + "layouts/layout.html", ) } -func templatesLayoutsLayoutHtml() (*asset, error) { - bytes, err := templatesLayoutsLayoutHtmlBytes() +func layoutsLayoutHtml() (*asset, error) { + bytes, err := layoutsLayoutHtmlBytes() if err != nil { 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} 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( - _templatesLayoutsMylayoutHtml, - "templates/layouts/mylayout.html", + _layoutsMylayoutHtml, + "layouts/mylayout.html", ) } -func templatesLayoutsMylayoutHtml() (*asset, error) { - bytes, err := templatesLayoutsMylayoutHtmlBytes() +func layoutsMylayoutHtml() (*asset, error) { + bytes, err := layoutsMylayoutHtmlBytes() if err != nil { 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} 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( - _templatesPage1Html, - "templates/page1.html", + _page1Html, + "page1.html", ) } -func templatesPage1Html() (*asset, error) { - bytes, err := templatesPage1HtmlBytes() +func page1Html() (*asset, error) { + bytes, err := page1HtmlBytes() if err != nil { 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} 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( - _templatesPartialsPage1_partial1Html, - "templates/partials/page1_partial1.html", + _partialsPage1_partial1Html, + "partials/page1_partial1.html", ) } -func templatesPartialsPage1_partial1Html() (*asset, error) { - bytes, err := templatesPartialsPage1_partial1HtmlBytes() +func partialsPage1_partial1Html() (*asset, error) { + bytes, err := partialsPage1_partial1HtmlBytes() if err != nil { 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} return a, nil } @@ -212,10 +300,10 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "templates/layouts/layout.html": templatesLayoutsLayoutHtml, - "templates/layouts/mylayout.html": templatesLayoutsMylayoutHtml, - "templates/page1.html": templatesPage1Html, - "templates/partials/page1_partial1.html": templatesPartialsPage1_partial1Html, + "layouts/layout.html": layoutsLayoutHtml, + "layouts/mylayout.html": layoutsMylayoutHtml, + "page1.html": page1Html, + "partials/page1_partial1.html": partialsPage1_partial1Html, } // AssetDir returns the file names below a certain @@ -259,15 +347,13 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "templates": {nil, map[string]*bintree{ - "layouts": {nil, map[string]*bintree{ - "layout.html": {templatesLayoutsLayoutHtml, map[string]*bintree{}}, - "mylayout.html": {templatesLayoutsMylayoutHtml, map[string]*bintree{}}, - }}, - "page1.html": {templatesPage1Html, map[string]*bintree{}}, - "partials": {nil, map[string]*bintree{ - "page1_partial1.html": {templatesPartialsPage1_partial1Html, map[string]*bintree{}}, - }}, + "layouts": {nil, map[string]*bintree{ + "layout.html": {layoutsLayoutHtml, map[string]*bintree{}}, + "mylayout.html": {layoutsMylayoutHtml, map[string]*bintree{}}, + }}, + "page1.html": {page1Html, map[string]*bintree{}}, + "partials": {nil, map[string]*bintree{ + "page1_partial1.html": {partialsPage1_partial1Html, map[string]*bintree{}}, }}, }} diff --git a/_examples/view/embedding-templates-into-app/main.go b/_examples/view/embedding-templates-into-app/main.go index da087a86..7158b16e 100644 --- a/_examples/view/embedding-templates-into-app/main.go +++ b/_examples/view/embedding-templates-into-app/main.go @@ -1,24 +1,22 @@ package main -import ( - "github.com/kataras/iris/v12" -) +import "github.com/kataras/iris/v12" func main() { 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.AddFunc("greet", func(s string) string { 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.Get("/", func(ctx iris.Context) { @@ -54,8 +52,3 @@ func main() { // http://localhost:8080/my/other 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`. diff --git a/_examples/view/template_ace_0/views/layouts/main.ace b/_examples/view/template_ace_0/views/layouts/main.ace index b3997c7c..58868659 100644 --- a/_examples/view/template_ace_0/views/layouts/main.ace +++ b/_examples/view/template_ace_0/views/layouts/main.ace @@ -3,4 +3,5 @@ html head title Main Page body + h1 Layout {{ yield }} \ No newline at end of file diff --git a/_examples/view/template_amber_0/main.go b/_examples/view/template_amber_0/main.go new file mode 100644 index 00000000..8d868ed8 --- /dev/null +++ b/_examples/view/template_amber_0/main.go @@ -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") +} diff --git a/_examples/view/template_amber_0/views/index.amber b/_examples/view/template_amber_0/views/index.amber new file mode 100644 index 00000000..ab89a92a --- /dev/null +++ b/_examples/view/template_amber_0/views/index.amber @@ -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 \ No newline at end of file diff --git a/_examples/view/template_amber_0/views/layouts/main.amber b/_examples/view/template_amber_0/views/layouts/main.amber new file mode 100644 index 00000000..b2f9ad6c --- /dev/null +++ b/_examples/view/template_amber_0/views/layouts/main.amber @@ -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 \ No newline at end of file diff --git a/_examples/view/template_amber_1_embedded/bindata.go b/_examples/view/template_amber_1_embedded/bindata.go new file mode 100644 index 00000000..1a797f84 --- /dev/null +++ b/_examples/view/template_amber_1_embedded/bindata.go @@ -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, "/")...)...) +} diff --git a/_examples/view/template_amber_1_embedded/main.go b/_examples/view/template_amber_1_embedded/main.go new file mode 100644 index 00000000..d2e3d591 --- /dev/null +++ b/_examples/view/template_amber_1_embedded/main.go @@ -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") +} diff --git a/_examples/view/template_blocks_1_embedded/bindata.go b/_examples/view/template_blocks_1_embedded/bindata.go index 465eb47c..2c284465 100644 --- a/_examples/view/template_blocks_1_embedded/bindata.go +++ b/_examples/view/template_blocks_1_embedded/bindata.go @@ -1,6 +1,6 @@ // 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/index.html // ../template_blocks_0/views/layouts/error.html @@ -14,6 +14,7 @@ import ( "fmt" "io" "io/ioutil" + "net/http" "os" "path/filepath" "strings" @@ -82,102 +83,188 @@ func (fi bindataFileInfo) Sys() interface{} { 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( - _views500Html, - "views/500.html", + __500Html, + "500.html", ) } -func views500Html() (*asset, error) { - bytes, err := views500HtmlBytes() +func _500Html() (*asset, error) { + bytes, err := _500HtmlBytes() if err != nil { 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} 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( - _viewsIndexHtml, - "views/index.html", + _indexHtml, + "index.html", ) } -func viewsIndexHtml() (*asset, error) { - bytes, err := viewsIndexHtmlBytes() +func indexHtml() (*asset, error) { + bytes, err := indexHtmlBytes() if err != nil { 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} 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( - _viewsLayoutsErrorHtml, - "views/layouts/error.html", + _layoutsErrorHtml, + "layouts/error.html", ) } -func viewsLayoutsErrorHtml() (*asset, error) { - bytes, err := viewsLayoutsErrorHtmlBytes() +func layoutsErrorHtml() (*asset, error) { + bytes, err := layoutsErrorHtmlBytes() if err != nil { 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} 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( - _viewsLayoutsMainHtml, - "views/layouts/main.html", + _layoutsMainHtml, + "layouts/main.html", ) } -func viewsLayoutsMainHtml() (*asset, error) { - bytes, err := viewsLayoutsMainHtmlBytes() +func layoutsMainHtml() (*asset, error) { + bytes, err := layoutsMainHtmlBytes() if err != nil { 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} 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( - _viewsPartialsFooterHtml, - "views/partials/footer.html", + _partialsFooterHtml, + "partials/footer.html", ) } -func viewsPartialsFooterHtml() (*asset, error) { - bytes, err := viewsPartialsFooterHtmlBytes() +func partialsFooterHtml() (*asset, error) { + bytes, err := partialsFooterHtmlBytes() if err != nil { 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} return a, nil } @@ -186,8 +273,8 @@ func viewsPartialsFooterHtml() (*asset, error) { // It returns an error if the asset could not be found or // could not be loaded. func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { + 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) @@ -212,8 +299,8 @@ func MustAsset(name string) []byte { // It returns an error if the asset could not be found or // could not be loaded. func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { + 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) @@ -234,11 +321,11 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "views/500.html": views500Html, - "views/index.html": viewsIndexHtml, - "views/layouts/error.html": viewsLayoutsErrorHtml, - "views/layouts/main.html": viewsLayoutsMainHtml, - "views/partials/footer.html": viewsPartialsFooterHtml, + "500.html": _500Html, + "index.html": indexHtml, + "layouts/error.html": layoutsErrorHtml, + "layouts/main.html": layoutsMainHtml, + "partials/footer.html": partialsFooterHtml, } // AssetDir returns the file names below a certain @@ -252,13 +339,13 @@ var _bindata = map[string]func() (*asset, error){ // 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("notexist") would return an error +// 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 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") + canonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(canonicalName, "/") for _, p := range pathList { node = node.Children[p] if node == nil { @@ -282,16 +369,14 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "views": {nil, map[string]*bintree{ - "500.html": {views500Html, map[string]*bintree{}}, - "index.html": {viewsIndexHtml, map[string]*bintree{}}, - "layouts": {nil, map[string]*bintree{ - "error.html": {viewsLayoutsErrorHtml, map[string]*bintree{}}, - "main.html": {viewsLayoutsMainHtml, map[string]*bintree{}}, - }}, - "partials": {nil, map[string]*bintree{ - "footer.html": {viewsPartialsFooterHtml, map[string]*bintree{}}, - }}, + "500.html": {_500Html, map[string]*bintree{}}, + "index.html": {indexHtml, map[string]*bintree{}}, + "layouts": {nil, map[string]*bintree{ + "error.html": {layoutsErrorHtml, map[string]*bintree{}}, + "main.html": {layoutsMainHtml, map[string]*bintree{}}, + }}, + "partials": {nil, map[string]*bintree{ + "footer.html": {partialsFooterHtml, map[string]*bintree{}}, }}, }} @@ -338,6 +423,6 @@ func RestoreAssets(dir, name string) error { } func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) + canonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) } diff --git a/_examples/view/template_blocks_1_embedded/main.go b/_examples/view/template_blocks_1_embedded/main.go index 020a2235..55f2b33d 100644 --- a/_examples/view/template_blocks_1_embedded/main.go +++ b/_examples/view/template_blocks_1_embedded/main.go @@ -2,15 +2,21 @@ package main import "github.com/kataras/iris/v12" -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata -prefix "../template_blocks_0" ../template_blocks_0/views/... +// $ 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_blocks_0/views" ../template_blocks_0/views/... // $ 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. - func main() { app := iris.New() - app.RegisterView(iris.Blocks("./views", ".html").Binary(Asset, AssetNames)) + app.RegisterView(iris.Blocks(AssetFile(), ".html")) app.Get("/", index) app.Get("/500", internalServerError) diff --git a/_examples/view/template_jet_1_embedded/README.md b/_examples/view/template_jet_1_embedded/README.md deleted file mode 100644 index bb78ada6..00000000 --- a/_examples/view/template_jet_1_embedded/README.md +++ /dev/null @@ -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. diff --git a/_examples/view/template_jet_1_embedded/bindata.go b/_examples/view/template_jet_1_embedded/bindata.go index 11989e7b..60528aff 100644 --- a/_examples/view/template_jet_1_embedded/bindata.go +++ b/_examples/view/template_jet_1_embedded/bindata.go @@ -1,5 +1,6 @@ -// Code generated by go-bindata. DO NOT EDIT. -// sources: +// Code generated by go-bindata. (@generated) DO NOT EDIT. + +// Package main generated by go-bindata.// sources: // views/includes/_partial.jet // views/includes/blocks.jet // views/index.jet @@ -12,6 +13,7 @@ import ( "fmt" "io" "io/ioutil" + "net/http" "os" "path/filepath" "strings" @@ -21,7 +23,7 @@ import ( 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) + return nil, fmt.Errorf("read %q: %v", name, err) } var buf bytes.Buffer @@ -29,7 +31,7 @@ func bindataRead(data []byte, name string) ([]byte, error) { clErr := gz.Close() if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) + return nil, fmt.Errorf("read %q: %v", name, err) } if clErr != nil { return nil, err @@ -50,101 +52,198 @@ type bindataFileInfo struct { 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 false + return fi.mode&os.ModeDir != 0 } + +// Sys return file is sys mode func (fi bindataFileInfo) Sys() interface{} { 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( - _viewsIncludes_partialJet, - "views/includes/_partial.jet", + _includes_partialJet, + "includes/_partial.jet", ) } -func viewsIncludes_partialJet() (*asset, error) { - bytes, err := viewsIncludes_partialJetBytes() +func includes_partialJet() (*asset, error) { + bytes, err := includes_partialJetBytes() if err != nil { 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} 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( - _viewsIncludesBlocksJet, - "views/includes/blocks.jet", + _includesBlocksJet, + "includes/blocks.jet", ) } -func viewsIncludesBlocksJet() (*asset, error) { - bytes, err := viewsIncludesBlocksJetBytes() +func includesBlocksJet() (*asset, error) { + bytes, err := includesBlocksJetBytes() if err != nil { 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} 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( - _viewsIndexJet, - "views/index.jet", + _indexJet, + "index.jet", ) } -func viewsIndexJet() (*asset, error) { - bytes, err := viewsIndexJetBytes() +func indexJet() (*asset, error) { + bytes, err := indexJetBytes() if err != nil { 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} 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( - _viewsLayoutsApplicationJet, - "views/layouts/application.jet", + _layoutsApplicationJet, + "layouts/application.jet", ) } -func viewsLayoutsApplicationJet() (*asset, error) { - bytes, err := viewsLayoutsApplicationJetBytes() +func layoutsApplicationJet() (*asset, error) { + bytes, err := layoutsApplicationJetBytes() if err != nil { 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} return a, nil } @@ -153,8 +252,8 @@ func viewsLayoutsApplicationJet() (*asset, error) { // It returns an error if the asset could not be found or // could not be loaded. func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { + 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) @@ -179,8 +278,8 @@ func MustAsset(name string) []byte { // It returns an error if the asset could not be found or // could not be loaded. func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { + 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) @@ -201,10 +300,10 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "views/includes/_partial.jet": viewsIncludes_partialJet, - "views/includes/blocks.jet": viewsIncludesBlocksJet, - "views/index.jet": viewsIndexJet, - "views/layouts/application.jet": viewsLayoutsApplicationJet, + "includes/_partial.jet": includes_partialJet, + "includes/blocks.jet": includesBlocksJet, + "index.jet": indexJet, + "layouts/application.jet": layoutsApplicationJet, } // AssetDir returns the file names below a certain @@ -218,13 +317,13 @@ var _bindata = map[string]func() (*asset, error){ // 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("notexist") would return an error +// 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 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") + canonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(canonicalName, "/") for _, p := range pathList { node = node.Children[p] if node == nil { @@ -248,15 +347,13 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "views": {nil, map[string]*bintree{ - "includes": {nil, map[string]*bintree{ - "_partial.jet": {viewsIncludes_partialJet, map[string]*bintree{}}, - "blocks.jet": {viewsIncludesBlocksJet, map[string]*bintree{}}, - }}, - "index.jet": {viewsIndexJet, map[string]*bintree{}}, - "layouts": {nil, map[string]*bintree{ - "application.jet": {viewsLayoutsApplicationJet, map[string]*bintree{}}, - }}, + "includes": {nil, map[string]*bintree{ + "_partial.jet": {includes_partialJet, map[string]*bintree{}}, + "blocks.jet": {includesBlocksJet, map[string]*bintree{}}, + }}, + "index.jet": {indexJet, map[string]*bintree{}}, + "layouts": {nil, map[string]*bintree{ + "application.jet": {layoutsApplicationJet, map[string]*bintree{}}, }}, }} @@ -303,6 +400,6 @@ func RestoreAssets(dir, name string) error { } func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) + canonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) } diff --git a/_examples/view/template_jet_1_embedded/main.go b/_examples/view/template_jet_1_embedded/main.go index 028fb7be..7e46d7e8 100644 --- a/_examples/view/template_jet_1_embedded/main.go +++ b/_examples/view/template_jet_1_embedded/main.go @@ -6,29 +6,31 @@ package main import ( "os" - "strings" "github.com/kataras/iris/v12" ) -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata ./views/... +// $ 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 "views" ./views/... // $ 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.Jet("./views", ".jet").Binary(Asset, AssetNames) + tmpl := iris.Jet(AssetFile(), ".jet") app.RegisterView(tmpl) app.Get("/", func(ctx iris.Context) { ctx.View("index.jet") }) - port := os.Getenv("PORT") - if len(port) == 0 { - port = ":8080" - } else if !strings.HasPrefix(":", port) { - port = ":" + port + addr := ":8080" + if port := os.Getenv("PORT"); port != "" { + addr = ":" + port } - app.Listen(port) + app.Listen(addr) } diff --git a/_examples/view/template_jet_3/main.go b/_examples/view/template_jet_3/main.go index 6928243e..a54e3bf8 100644 --- a/_examples/view/template_jet_3/main.go +++ b/_examples/view/template_jet_3/main.go @@ -9,8 +9,6 @@ import ( "github.com/kataras/iris/v12/view" ) -// https://github.com/kataras/iris/issues/1443 - func main() { 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 { path := a.Get(0).String() - s := fmt.Sprintf(` `, path) + s := fmt.Sprintf(``, path) return reflect.ValueOf(s) } func (ViewBuiler) Script(a view.JetArguments) reflect.Value { path := a.Get(0).String() - s := fmt.Sprintf(``, path) + s := fmt.Sprintf(``, path) return reflect.ValueOf(s) } diff --git a/_examples/view/template_jet_3/views/index.jet b/_examples/view/template_jet_3/views/index.jet index bfb1e3fa..6022ed37 100644 --- a/_examples/view/template_jet_3/views/index.jet +++ b/_examples/view/template_jet_3/views/index.jet @@ -1,5 +1,16 @@ {{ asset("./myasset.mp3")}}
-{{ style("my-stle.css")}} +{{ style("my-stle.css") | raw}}
-{{ script("my-script.js")}} \ No newline at end of file +{{ script("my-script.js") | raw}} + + + + + ./myasset.mp3 +
+ <link href="my-stle.css" rel="stylesheet"> +
+ <script src="my-script.js"></script> +
\ No newline at end of file diff --git a/_examples/view/template_pug_0/main.go b/_examples/view/template_pug_0/main.go index a7d38186..27c50f06 100644 --- a/_examples/view/template_pug_0/main.go +++ b/_examples/view/template_pug_0/main.go @@ -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 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() { app := iris.New() 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.Get("/", index) @@ -20,9 +28,15 @@ func main() { } func index(ctx iris.Context) { - ctx.ViewData("pageTitle", "My Index Page") - ctx.ViewData("youAreUsingJade", true) - // Q: why need extension .pug? - // A: Because you can register more than one view engine per Iris application. - ctx.View("index.pug") + job1 := Job{Employer: "Monash B", Role: "Honorary"} + 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) } diff --git a/_examples/view/template_pug_0/templates/index.pug b/_examples/view/template_pug_0/templates/index.pug index e0f39238..c6b21de2 100644 --- a/_examples/view/template_pug_0/templates/index.pug +++ b/_examples/view/template_pug_0/templates/index.pug @@ -1,25 +1,20 @@ -mixin withGo - | Generating Go html/template output. - doctype html html(lang="en") - head - title= .pageTitle - script(type='text/javascript'). - if (foo) { - bar(1 + 5) - } - body - h1 Jade - template engine - #container.col - if .youAreUsingJade - p {{ greet "iris user" }} - p You are amazing! - else - p Get on it! - p. - Jade is #[a(terse)] and simple - templating language with a - #[strong focus] on performance - and powerful features. - + withGo \ No newline at end of file + head + meta(charset="utf-8") + title Title + body + p ads + 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 }} \ No newline at end of file diff --git a/_examples/view/template_pug_1/main.go b/_examples/view/template_pug_1/main.go index 27c50f06..28186b14 100644 --- a/_examples/view/template_pug_1/main.go +++ b/_examples/view/template_pug_1/main.go @@ -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 -import "github.com/kataras/iris/v12" +import ( + "html/template" -type Person struct { - Name string - Age int - Emails []string - Jobs []*Job -} - -type Job struct { - Employer string - Role string -} + "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("" + s + ""), nil + }) + app.RegisterView(tmpl) app.Get("/", index) @@ -28,15 +24,5 @@ func main() { } func index(ctx iris.Context) { - job1 := Job{Employer: "Monash B", Role: "Honorary"} - 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) + ctx.View("index.pug") } diff --git a/_examples/view/template_pug_2/templates/footer.pug b/_examples/view/template_pug_1/templates/footer.pug similarity index 100% rename from _examples/view/template_pug_2/templates/footer.pug rename to _examples/view/template_pug_1/templates/footer.pug diff --git a/_examples/view/template_pug_2/templates/header.pug b/_examples/view/template_pug_1/templates/header.pug similarity index 100% rename from _examples/view/template_pug_2/templates/header.pug rename to _examples/view/template_pug_1/templates/header.pug diff --git a/_examples/view/template_pug_1/templates/index.pug b/_examples/view/template_pug_1/templates/index.pug index c6b21de2..ee11d9b4 100644 --- a/_examples/view/template_pug_1/templates/index.pug +++ b/_examples/view/template_pug_1/templates/index.pug @@ -1,20 +1,7 @@ doctype html -html(lang="en") - head - meta(charset="utf-8") - title Title - body - p ads - 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 }} \ No newline at end of file +html + include header.pug + body + h1 My Site + p {{ bold "Welcome to my super lame site."}} + include footer.pug \ No newline at end of file diff --git a/_examples/view/template_pug_2/main.go b/_examples/view/template_pug_2/main.go deleted file mode 100644 index 28186b14..00000000 --- a/_examples/view/template_pug_2/main.go +++ /dev/null @@ -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("" + s + ""), nil - }) - - app.RegisterView(tmpl) - - app.Get("/", index) - - // http://localhost:8080 - app.Listen(":8080") -} - -func index(ctx iris.Context) { - ctx.View("index.pug") -} diff --git a/_examples/view/template_pug_2/templates/index.pug b/_examples/view/template_pug_2/templates/index.pug deleted file mode 100644 index ee11d9b4..00000000 --- a/_examples/view/template_pug_2/templates/index.pug +++ /dev/null @@ -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 \ No newline at end of file diff --git a/_examples/view/template_pug_3/bindata.go b/_examples/view/template_pug_2_embedded/bindata.go similarity index 51% rename from _examples/view/template_pug_3/bindata.go rename to _examples/view/template_pug_2_embedded/bindata.go index fb9d699a..1547e982 100644 --- a/_examples/view/template_pug_3/bindata.go +++ b/_examples/view/template_pug_2_embedded/bindata.go @@ -1,5 +1,6 @@ -// Code generated for package main by go-bindata DO NOT EDIT. (@generated) -// sources: +// Code generated by go-bindata. (@generated) DO NOT EDIT. + +// Package main generated by go-bindata.// sources: // templates/index.pug // templates/layout.pug package main @@ -10,6 +11,7 @@ import ( "fmt" "io" "io/ioutil" + "net/http" "os" "path/filepath" "strings" @@ -19,7 +21,7 @@ import ( 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) + return nil, fmt.Errorf("read %q: %v", name, err) } var buf bytes.Buffer @@ -27,7 +29,7 @@ func bindataRead(data []byte, name string) ([]byte, error) { clErr := gz.Close() if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) + return nil, fmt.Errorf("read %q: %v", name, err) } if clErr != nil { return nil, err @@ -63,7 +65,7 @@ func (fi bindataFileInfo) Mode() os.FileMode { return fi.mode } -// Mode return file modify time +// ModTime return file modify time func (fi bindataFileInfo) ModTime() time.Time { return fi.modTime } @@ -78,42 +80,128 @@ func (fi bindataFileInfo) Sys() interface{} { 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( - _templatesIndexPug, - "templates/index.pug", + _indexPug, + "index.pug", ) } -func templatesIndexPug() (*asset, error) { - bytes, err := templatesIndexPugBytes() +func indexPug() (*asset, error) { + bytes, err := indexPugBytes() if err != nil { 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} 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( - _templatesLayoutPug, - "templates/layout.pug", + _layoutPug, + "layout.pug", ) } -func templatesLayoutPug() (*asset, error) { - bytes, err := templatesLayoutPugBytes() +func layoutPug() (*asset, error) { + bytes, err := layoutPugBytes() if err != nil { 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} return a, nil } @@ -122,8 +210,8 @@ func templatesLayoutPug() (*asset, error) { // It returns an error if the asset could not be found or // could not be loaded. func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { + 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) @@ -148,8 +236,8 @@ func MustAsset(name string) []byte { // It returns an error if the asset could not be found or // could not be loaded. func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { + 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) @@ -170,8 +258,8 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "templates/index.pug": templatesIndexPug, - "templates/layout.pug": templatesLayoutPug, + "index.pug": indexPug, + "layout.pug": layoutPug, } // AssetDir returns the file names below a certain @@ -185,13 +273,13 @@ var _bindata = map[string]func() (*asset, error){ // 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("notexist") would return an error +// 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 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") + canonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(canonicalName, "/") for _, p := range pathList { node = node.Children[p] if node == nil { @@ -215,10 +303,8 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "templates": {nil, map[string]*bintree{ - "index.pug": {templatesIndexPug, map[string]*bintree{}}, - "layout.pug": {templatesLayoutPug, map[string]*bintree{}}, - }}, + "index.pug": {indexPug, map[string]*bintree{}}, + "layout.pug": {layoutPug, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory @@ -264,6 +350,6 @@ func RestoreAssets(dir, name string) error { } func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) + canonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) } diff --git a/_examples/view/template_pug_2_embedded/main.go b/_examples/view/template_pug_2_embedded/main.go new file mode 100644 index 00000000..300251ef --- /dev/null +++ b/_examples/view/template_pug_2_embedded/main.go @@ -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") +} diff --git a/_examples/view/template_pug_3/templates/index.pug b/_examples/view/template_pug_2_embedded/templates/index.pug similarity index 100% rename from _examples/view/template_pug_3/templates/index.pug rename to _examples/view/template_pug_2_embedded/templates/index.pug diff --git a/_examples/view/template_pug_3/templates/layout.pug b/_examples/view/template_pug_2_embedded/templates/layout.pug similarity index 100% rename from _examples/view/template_pug_3/templates/layout.pug rename to _examples/view/template_pug_2_embedded/templates/layout.pug diff --git a/_examples/view/template_pug_3/main.go b/_examples/view/template_pug_3/main.go deleted file mode 100644 index 68b05f42..00000000 --- a/_examples/view/template_pug_3/main.go +++ /dev/null @@ -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") -} diff --git a/core/router/api_builder.go b/core/router/api_builder.go index 49cc9aa6..2904a982 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -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. // -// Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/file-server -func (api *APIBuilder) HandleDir(requestPath string, fs http.FileSystem, opts ...DirOptions) (routes []*Route) { +// Usage: +// 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 if len(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) description := "file server" if d, ok := fs.(http.Dir); ok { diff --git a/core/router/party.go b/core/router/party.go index 25915887..98110f88 100644 --- a/core/router/party.go +++ b/core/router/party.go @@ -1,8 +1,6 @@ package router import ( - "net/http" - "github.com/kataras/iris/v12/context" "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. // - // Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/file-server - HandleDir(requestPath string, fs http.FileSystem, opts ...DirOptions) []*Route + // Usage: + // 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 // see context.ExecRoute(routeName) and diff --git a/go.mod b/go.mod index d3e1a08e..a5e164a9 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/iris-contrib/pongo2 v0.0.1 github.com/iris-contrib/schema v0.0.6 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/neffos v0.0.16 github.com/kataras/pio v0.0.10 diff --git a/view/README.md b/view/README.md index 17599f3d..083ea5b2 100644 --- a/view/README.md +++ b/view/README.md @@ -115,11 +115,19 @@ func hi(ctx iris.Context) { ## Embedded -View engine supports bundled(https://github.com/go-bindata/go-bindata) template files too. -`go-bindata` gives you two functions, `Assset` and `AssetNames`, -these can be set to each of the template engines using the `.Binary` function. +View engine supports bundled(https://github.com/go-bindata/go-bindata) template files too. Latest +`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")`. -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 package main @@ -128,12 +136,7 @@ import "github.com/kataras/iris/v12" func main() { app := iris.New() - // $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata - // $ 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.RegisterView(iris.HTML(AssetFile(), ".html")) app.Get("/", hi) // http://localhost:8080 diff --git a/view/ace.go b/view/ace.go index d658fd0c..a62cd959 100644 --- a/view/ace.go +++ b/view/ace.go @@ -1,7 +1,6 @@ package view import ( - "path" "sync" "github.com/yosssi/ace" @@ -13,8 +12,13 @@ import ( // The given "extension" MUST begin with a dot. // // 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) @@ -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( ace.NewFile(name, text), diff --git a/view/amber.go b/view/amber.go index 0bdecb41..b8c08acd 100644 --- a/view/amber.go +++ b/view/amber.go @@ -4,6 +4,7 @@ import ( "fmt" "html/template" "io" + "net/http" "os" "path/filepath" "strings" @@ -14,11 +15,10 @@ import ( // AmberEngine contains the amber view engine structure. type AmberEngine struct { + fs http.FileSystem // files configuration - directory string + rootDir 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 // rmu sync.RWMutex // locks for `ExecuteWiter` when `reload` is true. @@ -33,9 +33,15 @@ var ( // Amber creates and returns a new amber view engine. // 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{ - directory: directory, + fs: getFS(fs), + rootDir: "/", extension: extension, templateCache: make(map[string]*template.Template), funcs: make(map[string]interface{}), @@ -44,20 +50,18 @@ func Amber(directory, extension string) *AmberEngine { 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. func (s *AmberEngine) Ext() string { 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, // use it when you're in development and you're boring of restarting // 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. func (s *AmberEngine) Load() error { - if s.assetFn != nil && s.namesFn != nil { - // 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 amber templates from directory. -func (s *AmberEngine) loadDirectory() error { - dir, extension := s.directory, s.extension - - opt := amber.DirOptions{} - opt.Recursive = true + s.rmu.Lock() + defer s.rmu.Unlock() // prepare the global amber funcs funcs := template.FuncMap{} @@ -125,84 +105,52 @@ func (s *AmberEngine) loadDirectory() error { } 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 - if err == nil { - s.templateCache = make(map[string]*template.Template) - for k, v := range templates { - 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 + opts := amber.Options{ + PrettyPrint: false, + LineNumbers: false, + VirtualFilesystem: s.fs, } - for k, v := range s.funcs { - funcs[k] = v - } - - if len(virtualDirectory) > 0 { - if virtualDirectory[0] == '.' { // first check for .wrong - virtualDirectory = virtualDirectory[1:] + return walk(s.fs, s.rootDir, func(path string, info os.FileInfo, err error) error { + if info == nil || info.IsDir() { + return nil } - 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() - - 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 + if s.extension != "" { + if !strings.HasSuffix(path, s.extension) { + return nil } - - 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 { - tmpl, ok := s.templateCache[relativeName] - if ok { + if s.reload { + s.rmu.RLock() + defer s.rmu.RUnlock() + } + + if tmpl, ok := s.templateCache[relativeName]; ok { return tmpl } + 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 { // re-parse the templates if reload is enabled. 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 { return err } @@ -224,5 +168,5 @@ func (s *AmberEngine) ExecuteWriter(w io.Writer, filename string, layout string, 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} } diff --git a/view/blocks.go b/view/blocks.go index e03beae2..23bc0c74 100644 --- a/view/blocks.go +++ b/view/blocks.go @@ -39,8 +39,28 @@ func WrapBlocks(v *blocks.Blocks) *BlocksEngine { // The given "extension" MUST begin with a dot. // // 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 @@ -66,18 +86,6 @@ func (s *BlocksEngine) AddLayoutFunc(funcName string, funcBody interface{}) *Blo 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 // the {{ template "content" . }} to render the main template. // diff --git a/view/django.go b/view/django.go index e77f8860..495202d8 100644 --- a/view/django.go +++ b/view/django.go @@ -2,9 +2,8 @@ package view import ( "bytes" - "fmt" "io" - "io/ioutil" + "net/http" "os" stdPath "path" "path/filepath" @@ -60,41 +59,40 @@ var AsValue = pongo2.AsValue var AsSafeValue = pongo2.AsSafeValue type tDjangoAssetLoader struct { - baseDir string - assetGet func(name string) ([]byte, error) + rootDir string + fs http.FileSystem } // 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. -func (dal *tDjangoAssetLoader) Abs(base, name string) string { +func (l *tDjangoAssetLoader) Abs(base, name string) string { if stdPath.IsAbs(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. -func (dal *tDjangoAssetLoader) Get(path string) (io.Reader, error) { +func (l *tDjangoAssetLoader) Get(path string) (io.Reader, error) { if stdPath.IsAbs(path) { path = path[1:] } - res, err := dal.assetGet(path) + res, err := asset(l.fs, path) if err != nil { return nil, err } - return bytes.NewBuffer(res), nil + return bytes.NewReader(res), nil } // DjangoEngine contains the django view engine structure. type DjangoEngine struct { + fs http.FileSystem // files configuration - directory string + rootDir 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 // rmu sync.RWMutex // locks for filters, globals and `ExecuteWiter` when `reload` is true. @@ -102,7 +100,6 @@ type DjangoEngine struct { filters map[string]FilterFunction // globals share context fields between templates. globals map[string]interface{} - mu sync.Mutex // locks for template cache templateCache map[string]*pongo2.Template } @@ -113,9 +110,15 @@ var ( // Django creates and returns a new django view engine. // 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{ - directory: directory, + fs: getFS(fs), + rootDir: "/", extension: extension, globals: make(map[string]interface{}), filters: make(map[string]FilterFunction), @@ -125,20 +128,18 @@ func Django(directory, extension string) *DjangoEngine { 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. func (s *DjangoEngine) Ext() string { 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, // use it when you're in development and you're boring of restarting // 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. func (s *DjangoEngine) Load() error { - if s.assetFn != nil && s.namesFn != nil { - // 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 := pongo2.NewSet("", &tDjangoAssetLoader{fs: s.fs, rootDir: s.rootDir}) set.Globals = getPongoContext(s.globals) - s.mu.Lock() - defer s.mu.Unlock() + s.rmu.Lock() + defer s.rmu.Unlock() - // Walk the supplied directory and compile any files that match our extension list. - 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). + return walk(s.fs, s.rootDir, func(path string, info os.FileInfo, err error) error { if info == nil || info.IsDir() { - } else { + return nil + } - rel, err := filepath.Rel(dir, path) - if err != nil { - templateErr = err - return err + if s.extension != "" { + if !strings.HasSuffix(path, s.extension) { + return nil } - - 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 { return err } - ext := filepath.Ext(rel) - if ext == virtualExtension { - - 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 + name := strings.TrimPrefix(path, "/") + s.templateCache[name], err = set.FromBytes(buf) + return err + }) } // 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 { - s.mu.Lock() + if s.reload { + s.rmu.RLock() + defer s.rmu.RUnlock() + } - tmpl, ok := s.templateCache[relativeName] - - if ok { - s.mu.Unlock() + if tmpl, ok := s.templateCache[relativeName]; ok { return tmpl } - s.mu.Unlock() 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 { // re-parse the templates if reload is enabled. 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 { return err } @@ -380,5 +275,5 @@ func (s *DjangoEngine) ExecuteWriter(w io.Writer, filename string, layout string return tmpl.ExecuteWriter(getPongoContext(bindingData), w) } - return fmt.Errorf("template with name: %s ddoes not exist in the dir: %s", filename, s.directory) + return ErrNotExist{filename, false} } diff --git a/view/fs.go b/view/fs.go new file mode 100644 index 00000000..0732724b --- /dev/null +++ b/view/fs.go @@ -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)) +} diff --git a/view/handlebars.go b/view/handlebars.go index 45f8b99b..3c4017b3 100644 --- a/view/handlebars.go +++ b/view/handlebars.go @@ -3,7 +3,7 @@ package view import ( "fmt" "io" - "io/ioutil" + "net/http" "os" "path/filepath" "strings" @@ -14,15 +14,16 @@ import ( // HandlebarsEngine contains the handlebars view engine structure. type HandlebarsEngine struct { + fs http.FileSystem // files configuration - directory string + rootDir 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 // if true, each time the ExecuteWriter is called the templates will be reloaded. // parser configuration layout string - rmu sync.RWMutex // locks for helpers and `ExecuteWiter` when `reload` is true. + rmu sync.RWMutex helpers map[string]interface{} templateCache map[string]*raymond.Template } @@ -34,9 +35,15 @@ var ( // Handlebars creates and returns a new handlebars view engine. // 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{ - directory: directory, + fs: getFS(fs), + rootDir: "/", extension: extension, templateCache: make(map[string]*raymond.Template), helpers: make(map[string]interface{}), @@ -54,20 +61,18 @@ func Handlebars(directory, extension string) *HandlebarsEngine { 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. func (s *HandlebarsEngine) Ext() string { 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, // use it when you're in development and you're boring of restarting // 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. func (s *HandlebarsEngine) Load() error { - if s.assetFn != nil && s.namesFn != nil { - // embedded - return s.loadAssets() - } + s.rmu.Lock() + defer s.rmu.Unlock() - // 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 if len(s.templateCache) == 0 && s.helpers != nil { raymond.RegisterHelpers(s.helpers) } - dir, extension := s.directory, s.extension - - // 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 { + return walk(s.fs, s.rootDir, func(path string, info os.FileInfo, _ error) error { if info == nil || info.IsDir() { 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 err } - ext := filepath.Ext(rel) - if ext == extension { - buf, err := ioutil.ReadFile(path) - contents := string(buf) - - 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 + name := strings.TrimPrefix(path, "/") + tmpl, err := raymond.Parse(string(buf)) + if err != nil { + return err } + s.templateCache[name] = tmpl + 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 { - tmpl, ok := s.templateCache[relativeName] - if !ok { - return nil + if s.reload { + s.rmu.RLock() + 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) { @@ -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 { // re-parse the templates if reload is enabled. 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 { return err } @@ -295,5 +213,5 @@ func (s *HandlebarsEngine) ExecuteWriter(w io.Writer, filename string, layout st 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} } diff --git a/view/html.go b/view/html.go index b6723735..63be0fb1 100644 --- a/view/html.go +++ b/view/html.go @@ -5,7 +5,7 @@ import ( "fmt" "html/template" "io" - "io/ioutil" + "net/http" "os" "path/filepath" "strings" @@ -14,14 +14,16 @@ import ( // HTMLEngine contains the html view engine structure. type HTMLEngine struct { + // the file system to load from. + fs http.FileSystem // files configuration - directory string + rootDir 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 // 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. + // 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 - options []string // text options + options []string // (text) template options left string right string layout string @@ -65,12 +67,16 @@ var emptyFuncs = template.FuncMap{ // The html engine used like the "html/template" standard go package // but with a lot of extra features. // 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{ - directory: directory, + fs: getFS(fs), + rootDir: "/", extension: extension, - assetFn: nil, - namesFn: nil, reload: false, left: "{{", right: "}}", @@ -82,20 +88,18 @@ func HTML(directory, extension string) *HTMLEngine { 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. func (s *HTMLEngine) Ext() string { 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, // use it when you're in development and you're boring of restarting // 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. func (s *HTMLEngine) Load() error { - // No need to make this with a complicated and "pro" way, just add lockers to the `ExecuteWriter`. - // if `Reload(true)` and add a note for non conc access on dev mode. - // atomic.StoreUint32(&s.isLoading, 1) - // s.rmu.Lock() - // defer func() { - // s.rmu.Unlock() - // atomic.StoreUint32(&s.isLoading, 0) - // }() + s.rmu.Lock() + defer s.rmu.Unlock() - if s.assetFn != nil && s.namesFn != nil { - // 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 = template.New(s.rootDir) 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() { - } else { - rel, err := filepath.Rel(dir, path) + return nil + } + + 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 { - templateErr = 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 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) { @@ -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 { // re-parse the templates if reload is enabled. 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 { return err } @@ -505,14 +341,14 @@ func (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bind t := s.Templates.Lookup(name) 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) if layout = getLayout(layout, s.layout); layout != "" { lt := s.Templates.Lookup(layout) if lt == nil { - return fmt.Errorf("layout: %s does not exist in the dir: %s", name, s.directory) + return ErrNotExist{name, true} } s.layoutFuncsFor(lt, name, bindingData) diff --git a/view/jet.go b/view/jet.go index afab0c79..7c309306 100644 --- a/view/jet.go +++ b/view/jet.go @@ -3,8 +3,7 @@ package view import ( "fmt" "io" - "os" - "path" + "net/http" "path/filepath" "reflect" "strings" @@ -18,9 +17,10 @@ const jetEngineName = "jet" // JetEngine is the jet template parser's view engine. type JetEngine struct { - directory string + fs http.FileSystem + rootDir string extension string - // physical system files or app-embedded, see `Binary(..., ...)`. Defaults to file system on initialization. + loader jet.Loader developmentMode bool @@ -51,11 +51,12 @@ var jetExtensions = [...]string{ // Jet creates and returns a new jet view engine. // The given "extension" MUST begin with a dot. -func Jet(directory, extension string) *JetEngine { - // if _, err := os.Stat(directory); os.IsNotExist(err) { - // panic(err) - // } - +// +// Usage: +// 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 for _, ext := range jetExtensions { if ext == extension { @@ -69,9 +70,9 @@ func Jet(directory, extension string) *JetEngine { } s := &JetEngine{ - directory: directory, + rootDir: "/", extension: extension, - loader: jet.NewOSFileSystemLoader(directory), + loader: &jetLoader{fs: getFS(fs)}, jetDataContextKey: "_jet", } @@ -83,6 +84,13 @@ func (s *JetEngine) String() string { 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. func (s *JetEngine) Ext() string { 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 -// multi.Loader or httpfs.Loader of the jet subpackages, -// overrides any previous loader may set by `Binary` or the default. -// Should act before `Load` or `iris.Application#RegisterView`. +// multi.Loader or httpfs.Loader. func (s *JetEngine) SetLoader(loader jet.Loader) *JetEngine { s.loader = loader return s } -// 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. -// 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 jetLoader struct { + fs http.FileSystem } -type ( - 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 _ jet.Loader = (*jetLoader)(nil) -var ( - _ jet.Loader = (*embeddedLoader)(nil) - _ io.ReadCloser = (*embeddedFile)(nil) -) - -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 +// Open opens a file from file system. +func (l *jetLoader) Open(name string) (io.ReadCloser, error) { + return l.fs.Open(name) } // 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 -func (l *embeddedLoader) Exists(name string) (string, bool) { - name = path.Join(l.vdir, filepath.ToSlash(name)) - - if _, ok := l.names[name]; ok { +func (l *jetLoader) Exists(name string) (string, bool) { + if _, err := l.fs.Open(name); err == nil { return name, true } diff --git a/view/pug.go b/view/pug.go index 2182aaac..cb590740 100644 --- a/view/pug.go +++ b/view/pug.go @@ -2,9 +2,6 @@ package view import ( "bytes" - "io/ioutil" - "path" - "strings" "github.com/iris-contrib/jade" ) @@ -16,26 +13,23 @@ import ( // // 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: // 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_2 // https://github.com/kataras/iris/tree/master/_examples/view/template_pug_3 -func Pug(directory, extension string) *HTMLEngine { - s := HTML(directory, extension) +func Pug(fs interface{}, extension string) *HTMLEngine { + s := HTML(fs, extension) s.middleware = func(name string, text []byte) (contents string, err error) { - name = path.Join(path.Clean(directory), name) tmpl := jade.New(name) tmpl.ReadFunc = func(name string) ([]byte, error) { - if !strings.HasPrefix(path.Clean(name), path.Clean(directory)) { - name = path.Join(directory, name) - } - - if s.assetFn != nil { - return s.assetFn(name) - } - return ioutil.ReadFile(name) + return asset(s.fs, name) } // 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: // e.g. instead of extends "templates/layout.pug" we use "layout.pug" // so contents of templates are independent of their root location. - exec, err := tmpl.Parse(text) if err != nil { return diff --git a/view/view.go b/view/view.go index 05abdf78..7edbe4a6 100644 --- a/view/view.go +++ b/view/view.go @@ -19,6 +19,21 @@ type ( 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 // load the correct templates // for each of the registered view engines.