1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-02 09:47:17 +00:00

20 days of unstoppable work. Waiting fo go 1.8, I didn't finish yet, some touches remains.

Former-commit-id: ed84f99c89f43fe5e980a8e6d0ee22c186f0e1b9
This commit is contained in:
Gerasimos (Makis) Maropoulos
2017-02-14 05:54:11 +02:00
parent 2b2a205e63
commit 244a59e055
108 changed files with 9016 additions and 7596 deletions

View File

@@ -0,0 +1,93 @@
package main
import (
"encoding/xml"
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/gorillamux"
"gopkg.in/kataras/iris.v6/adaptors/view"
)
// ExampleXML just a test struct to view represents xml content-type
type ExampleXML struct {
XMLName xml.Name `xml:"example"`
One string `xml:"one,attr"`
Two string `xml:"two,attr"`
}
func main() {
app := iris.New()
app.Adapt(iris.DevLogger())
app.Adapt(gorillamux.New())
app.Get("/data", func(ctx *iris.Context) {
ctx.Data(iris.StatusOK, []byte("Some binary data here."))
})
app.Get("/text", func(ctx *iris.Context) {
ctx.Text(iris.StatusOK, "Plain text here")
})
app.Get("/json", func(ctx *iris.Context) {
ctx.JSON(iris.StatusOK, map[string]string{"hello": "json"}) // or myjsonStruct{hello:"json}
})
app.Get("/jsonp", func(ctx *iris.Context) {
ctx.JSONP(iris.StatusOK, "callbackName", map[string]string{"hello": "jsonp"})
})
app.Get("/xml", func(ctx *iris.Context) {
ctx.XML(iris.StatusOK, ExampleXML{One: "hello", Two: "xml"}) // or iris.Map{"One":"hello"...}
})
app.Get("/markdown", func(ctx *iris.Context) {
ctx.Markdown(iris.StatusOK, "# Hello Dynamic Markdown Iris")
})
app.Adapt(view.HTML("./templates", ".html"))
app.Get("/template", func(ctx *iris.Context) {
ctx.MustRender(
"hi.html", // the file name of the template relative to the './templates'
iris.Map{"Name": "Iris"}, // the .Name inside the ./templates/hi.html
iris.Map{"gzip": false}, // enable gzip for big files
)
})
// ------ first customization without even the need of *Context or a Handler--------
//
// Custom new content-/type:
// app.Adapt(iris.RenderPolicy(func(out io.Writer, name string, binding interface{}, options ...map[string]interface{}) (error, bool) {
// if name == "customcontent-type" {
//
// // some very advanced things here:
// out.Write([]byte(binding.(string)))
// return nil, true
// }
// return nil, false
// }))
//
// app.Get("/custom", func(ctx *iris.Context) {
// ctx.RenderWithStatus(iris.StatusOK, // or MustRender
// "customcontent-type",
// "my custom content here!",
// )
// })
//
// ---- second -----------------------------------------------------------------------
//
// Override the defaults (the json,xml,jsonp,text,data and so on), an existing content-type:
// app.Adapt(iris.RenderPolicy(func(out io.Writer, name string, binding interface{}, options ...map[string]interface{}) (error, bool) {
// if name == "text/plain" {
// out.Write([]byte("From the custom text/plain renderer: " + binding.(string)))
// return nil, true
// }
//
// return nil, false
// }))
// // the context.Text's behaviors was changed now by your custom renderer.
//
app.Listen(":8080")
}

View File

@@ -0,0 +1,8 @@
<html>
<head>
<title>Hi Iris</title>
</head>
<body>
<h1>Hi {{.Name}} </h1>
</body>
</html>

View File

@@ -0,0 +1,237 @@
// Code generated by go-bindata.
// sources:
// templates/hi.html
// DO NOT EDIT!
package main
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"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
}
func (fi bindataFileInfo) Name() string {
return fi.name
}
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
func (fi bindataFileInfo) IsDir() bool {
return false
}
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
var _templatesHiHtml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xb2\xc9\x28\xc9\xcd\xb1\xe3\xe5\xb2\xc9\x48\x4d\x4c\x01\xd1\x25\x99\x25\x39\xa9\x76\x1e\x99\x0a\x9e\x45\x99\xc5\x0a\xd1\x21\x1e\xae\x0a\x21\x9e\x21\x3e\xae\xb1\x36\xfa\x10\x29\xa0\x1a\x7d\x98\xe2\xa4\xfc\x94\x4a\x20\xcd\x69\x93\x61\x08\xd2\x52\x5d\xad\xe7\x97\x98\x9b\x5a\x5b\x0b\x52\x03\x95\x03\x2a\x86\xd8\x00\x08\x00\x00\xff\xff\xed\x0e\xad\x42\x6a\x00\x00\x00")
func templatesHiHtmlBytes() ([]byte, error) {
return bindataRead(
_templatesHiHtml,
"templates/hi.html",
)
}
func templatesHiHtml() (*asset, error) {
bytes, err := templatesHiHtmlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "templates/hi.html", size: 106, mode: os.FileMode(438), modTime: time.Unix(1468907204, 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) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; 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) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; 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){
"templates/hi.html": templatesHiHtml,
}
// 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("notexist") 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, "/")
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{
"templates": {nil, map[string]*bintree{
"hi.html": {templatesHiHtml, 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 {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

View File

@@ -0,0 +1,24 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
)
func main() {
app := iris.New()
app.Adapt(iris.DevLogger())
app.Adapt(httprouter.New())
//$ go-bindata ./templates/...
// templates are not used, you can delete the folder and run the example
app.Adapt(view.HTML("./templates", ".html").Binary(Asset, AssetNames))
app.Get("/hi", hi)
app.Listen(":8080")
}
func hi(ctx *iris.Context) {
ctx.MustRender("hi.html", struct{ Name string }{Name: "iris"})
}

View File

@@ -0,0 +1,8 @@
<html>
<head>
<title>Hi Iris [THE TITLE]</title>
</head>
<body>
<h1>Hi {{.Name}}
</body>
</html>

View File

@@ -0,0 +1,23 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
)
func main() {
app := iris.New(iris.Configuration{Gzip: false, Charset: "UTF-8"}) // defaults to these
app.Adapt(iris.DevLogger())
app.Adapt(httprouter.New())
app.Adapt(view.HTML("./templates", ".html"))
app.Get("/hi", hi)
app.Listen(":8080")
}
func hi(ctx *iris.Context) {
ctx.MustRender("hi.html", struct{ Name string }{Name: "iris"})
}

View File

@@ -0,0 +1,8 @@
<html>
<head>
<title>Hi Iris</title>
</head>
<body>
<h1>Hi {{.Name}} </h1>
</body>
</html>

View File

@@ -0,0 +1,32 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
)
type mypage struct {
Title string
Message string
}
func main() {
app := iris.New()
app.Adapt(iris.DevLogger())
app.Adapt(httprouter.New())
tmpl := view.HTML("./templates", ".html")
tmpl.Layout("layout.html")
app.Adapt(tmpl)
app.Get("/", func(ctx *iris.Context) {
ctx.Render("mypage.html", mypage{"My Page title", "Hello world!"}, iris.Map{"gzip": true})
// Note that: you can pass "layout" : "otherLayout.html" to bypass the config's Layout property
// or iris.NoLayout to disable layout on this render action.
// third is an optional parameter
})
app.Listen(":8080")
}

View File

@@ -0,0 +1,11 @@
<html>
<head>
<title>My Layout</title>
</head>
<body>
<h1>Body is:</h1>
<!-- Render the current template here -->
{{ yield }}
</body>
</html>

View File

@@ -0,0 +1,4 @@
<h1>
Title: {{.Title}}
</h1>
<h3>Message: {{.Message}} </h3>

View File

@@ -0,0 +1,3 @@
## Info
This folder examines the {{render "dir/templatefilename"}} functionality to manually render any template inside any template

View File

@@ -0,0 +1,49 @@
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
)
func main() {
app := iris.New()
app.Adapt(iris.DevLogger())
app.Adapt(httprouter.New())
tmpl := view.HTML("./templates", ".html")
tmpl.Layout("layouts/layout.html")
tmpl.Funcs(map[string]interface{}{
"greet": func(s string) string {
return "Greetings " + s + "!"
},
})
app.Adapt(tmpl)
app.Get("/", func(ctx *iris.Context) {
if err := ctx.Render("page1.html", nil); err != nil {
println(err.Error())
}
})
// remove the layout for a specific route
app.Get("/nolayout", func(ctx *iris.Context) {
if err := ctx.Render("page1.html", nil, iris.RenderOptions{"layout": iris.NoLayout}); err != nil {
println(err.Error())
}
})
// set a layout for a party, .Layout should be BEFORE any Get or other Handle party's method
my := app.Party("/my").Layout("layouts/mylayout.html")
{
my.Get("/", func(ctx *iris.Context) {
ctx.MustRender("page1.html", nil)
})
my.Get("/other", func(ctx *iris.Context) {
ctx.MustRender("page1.html", nil)
})
}
app.Listen(":8080")
}

View File

@@ -0,0 +1,12 @@
<html>
<head>
<title>Layout</title>
</head>
<body>
<h1>This is the global layout</h1>
<br />
<!-- Render the current template here -->
{{ yield }}
</body>
</html>

View File

@@ -0,0 +1,12 @@
<html>
<head>
<title>my Layout</title>
</head>
<body>
<h1>This is the layout for the /my/ and /my/other routes only</h1>
<br />
<!-- Render the current template here -->
{{ yield }}
</body>
</html>

View File

@@ -0,0 +1,7 @@
<div style="background-color: black; color: blue">
<h1>Page 1 {{ greet "iris developer"}}</h1>
{{ render "partials/page1_partial1.html"}}
</div>

View File

@@ -0,0 +1,3 @@
<div style="background-color: white; color: red">
<h1>Page 1's Partial 1</h1>
</div>

View File

@@ -0,0 +1,53 @@
// Package main an example on how to naming your routes & use the custom 'url' HTML Template Engine, same for other template engines.
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/gorillamux"
"gopkg.in/kataras/iris.v6/adaptors/view"
)
func main() {
app := iris.New()
app.Adapt(iris.DevLogger())
app.Adapt(gorillamux.New())
app.Adapt(view.HTML("./templates", ".html"))
app.Get("/mypath", emptyHandler).ChangeName("my-page1")
app.Get("/mypath2/{param1}/{param2}", emptyHandler).ChangeName("my-page2")
app.Get("/mypath3/{param1}/statichere/{param2}", emptyHandler).ChangeName("my-page3")
app.Get("/mypath4/{param1}/statichere/{param2}/{otherparam}/{something:.*}", emptyHandler).ChangeName("my-page4")
// same with Handle/Func
app.HandleFunc("GET", "/mypath5/{param1}/statichere/{param2}/{otherparam}/anything/{something:.*}", emptyHandler).ChangeName("my-page5")
app.Get("/mypath6/{param1}/{param2}/staticParam/{param3AfterStatic}", emptyHandler).ChangeName("my-page6")
app.Get("/", func(ctx *iris.Context) {
// for /mypath6...
paramsAsArray := []string{"param1", "theParam1",
"param2", "theParam2",
"param3AfterStatic", "theParam3"}
if err := ctx.Render("page.html", iris.Map{"ParamsAsArray": paramsAsArray}); err != nil {
panic(err)
}
})
app.Get("/redirect/{namedRoute}", func(ctx *iris.Context) {
routeName := ctx.Param("namedRoute")
println("The full uri of " + routeName + "is: " + app.URL(routeName))
// if routeName == "my-page1"
// prints: The full uri of my-page1 is: http://127.0.0.1:8080/mypath
ctx.RedirectTo(routeName)
// http://127.0.0.1:8080/redirect/my-page1 will redirect to -> http://127.0.0.1:8080/mypath
})
app.Listen("localhost:8080")
}
func emptyHandler(ctx *iris.Context) {
ctx.Writef("Hello from %s.", ctx.Path())
}

View File

@@ -0,0 +1,27 @@
<a href="{{url "my-page1"}}">http://127.0.0.1:8080/mypath</a>
<br />
<br />
<a href="{{url "my-page2" "param1" "theParam1" "param2" "theParam2"}}">http://localhost:8080/mypath2/:param1/:param2</a>
<br />
<br />
<a href="{{url "my-page3" "param1" "theParam1" "param2" "theParam2AfterStatic"}}">
http://localhost:8080/mypath3/:param1/statichere/:param2</a>
<br />
<br />
<a href="{{url "my-page4" "param1" "theParam1" "param2" "theparam2AfterStatic" "otherparam" "otherParam" "something" "matchAnything"}}">http://localhost/mypath4/:param1/statichere/:param2/:otherparam/*something</a>
<br />
<br />
<a href="{{url "my-page5" "param1" "theParam1" "param2" "theParam2AfterStatic" "otherparam" "otherParam" "something" "matchAnythingAfterStatic"}}">
http://localhost:8080/mypath5/:param1/statichere/:param2/:otherparam/anything/*anything</a>
<br />
<br />
<a href={{url "my-page6" .ParamsAsArray }}>http://localhost:8080/mypath6/{param1}/{param2}/staticParam/{param3AfterStatic}</a>

View File

@@ -0,0 +1,32 @@
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
::1 localhost
#-IRIS-For development machine, you have to configure your dns also for online, search google how to do it if you don't know
127.0.0.1 username1.127.0.0.1
127.0.0.1 username2.127.0.0.1
127.0.0.1 username3.127.0.0.1
127.0.0.1 username4.127.0.0.1
127.0.0.1 username5.127.0.0.1
# note that you can always use custom subdomains
#-END IRIS-
# Windows: Drive:/Windows/system32/drivers/etc/hosts, on Linux: /etc/hosts

View File

@@ -0,0 +1,53 @@
// Package main an example on how to naming your routes & use the custom 'url' HTML Template Engine, same for other template engines.
package main
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/gorillamux"
"gopkg.in/kataras/iris.v6/adaptors/view"
)
func main() {
app := iris.New()
app.Adapt(iris.DevLogger())
app.Adapt(gorillamux.New())
app.Adapt(view.HTML("./templates", ".html"))
app.Get("/mypath", emptyHandler).ChangeName("my-page1")
app.Get("/mypath2/{param1}/{param2}", emptyHandler).ChangeName("my-page2")
app.Get("/mypath3/{param1}/statichere/{param2}", emptyHandler).ChangeName("my-page3")
app.Get("/mypath4/{param1}/statichere/{param2}/{otherparam}/{something:.*}", emptyHandler).ChangeName("my-page4")
// same with Handle/Func
app.HandleFunc("GET", "/mypath5/{param1}/statichere/{param2}/{otherparam}/anything/{something:.*}", emptyHandler).ChangeName("my-page5")
app.Get("/mypath6/{param1}/{param2}/staticParam/{param3AfterStatic}", emptyHandler).ChangeName("my-page6")
app.Get("/", func(ctx *iris.Context) {
// for /mypath6...
paramsAsArray := []string{"param1", "theParam1",
"param2", "theParam2",
"param3AfterStatic", "theParam3"}
if err := ctx.Render("page.html", iris.Map{"ParamsAsArray": paramsAsArray}); err != nil {
panic(err)
}
})
app.Get("/redirect/{namedRoute}", func(ctx *iris.Context) {
routeName := ctx.Param("namedRoute")
println("The full uri of " + routeName + "is: " + app.URL(routeName))
// if routeName == "my-page1"
// prints: The full uri of my-page1 is: http://127.0.0.1:8080/mypath
ctx.RedirectTo(routeName)
// http://127.0.0.1:8080/redirect/my-page1 will redirect to -> http://127.0.0.1:8080/mypath
})
app.Listen("localhost:8080")
}
func emptyHandler(ctx *iris.Context) {
ctx.Writef("Hello from %s.", ctx.Path())
}

View File

@@ -0,0 +1,16 @@
<!-- the only difference between normal named routes and dynamic subdomains named routes is that the first argument of url
is the subdomain part instead of named parameter-->
<a href="{{url "dynamic-subdomain1" "username1"}}">username1.127.0.0.1:8080/mypath</a>
<br />
<br />
<a href="{{url "dynamic-subdomain2" "username2" "theParam1" "theParam2"}}">username2.127.0.0.1:8080/mypath2/:param1/:param2</a>
<br />
<br />
<a href="{{url "dynamic-subdomain3" "username3" "theParam1" "theParam2AfterStatic"}}">username3.127.0.0.1:8080/mypath3/:param1/statichere/:param2</a>
<br />
<br />
<a href="{{url "dynamic-subdomain4" "username4" "theParam1" "theparam2AfterStatic" "otherParam" "matchAnything"}}">username4.127.0.0.1:8080/mypath4/:param1/statichere/:param2/:otherparam/*something</a>
<br />
<br />
<a href="{{url "dynamic-subdomain5" .ParamsAsArray }}" >username5.127.0.0.1:8080/mypath6/:param1/:param2/staticParam/:param3AfterStatic</a>

136
adaptors/view/adaptor.go Normal file
View File

@@ -0,0 +1,136 @@
package view
import (
"io"
"strings"
"github.com/kataras/go-template"
"gopkg.in/kataras/iris.v6"
)
// Adaptor contains the common actions
// that all template engines share.
//
// We need to export that as it is without an interface
// because it may be used as a wrapper for a template engine
// that is not exists yet but community can create.
type Adaptor struct {
dir string
extension string
// for a .go template file lives inside the executable
assetFn func(name string) ([]byte, error)
namesFn func() []string
reload bool
engine template.Engine // used only on Adapt, we could make
//it as adaptEngine and pass a second parameter there but this would break the pattern.
}
// NewAdaptor returns a new general template engine policy adaptor.
func NewAdaptor(directory string, extension string, e template.Engine) *Adaptor {
return &Adaptor{
dir: directory,
extension: extension,
engine: e,
}
}
// Binary optionally, use it when template files are distributed
// inside the app executable (.go generated files).
func (h *Adaptor) Binary(assetFn func(name string) ([]byte, error), namesFn func() []string) *Adaptor {
h.assetFn = assetFn
h.namesFn = namesFn
return h
}
// Reload if setted to true the templates are reloading on each call,
// use it when you're in development and you're boring of restarting
// the whole app when you edit a template file
func (h *Adaptor) Reload(developmentMode bool) *Adaptor {
h.reload = developmentMode
return h
}
// Adapt adapts a template engine to the main Iris' policies.
// this specific Adapt is a multi-policies adaptors
// we use that instead of just return New() iris.RenderPolicy
// for two reasons:
// - the user may need to edit the adaptor's fields
// like Directory, Binary
// - we need to adapt an event policy to add the engine to the external mux
// and load it.
func (h *Adaptor) Adapt(frame *iris.Policies) {
mux := template.DefaultMux
// on the build state in order to have the shared funcs also
evt := iris.EventPolicy{
Build: func(s *iris.Framework) {
// mux has default to ./templates and .html ext
// no need for any checks here.
// the RenderPolicy will give a "no templates found on 'directory'"
// if user tries to use the context.Render without having the template files
// no need to panic here because we will use the html as the default template engine
// even if the user doesn't asks for
// or no? we had the defaults before... maybe better to give the user
// the oportunity to learn about the template's configuration
// (now 6.1.4 ) they are defaulted and users may don't know how and if they can change the configuration
// even if the book and examples covers these things, many times they're asking me on chat.............
// so no defaults? ok no defaults. This file then will be saved to /adaptors as with other template engines.
// simple.
mux.AddEngine(h.engine).
Directory(h.dir, h.extension).
Binary(h.assetFn, h.namesFn)
mux.Reload = h.reload
// notes for me: per-template engine funcs are setted by each template engine adaptor itself,
// here we will set the template funcs policy'.
// as I explain on the TemplateFuncsPolicy it exists in order to allow community to create plugins
// even by adding custom template funcs to their behaviors.
// We know that iris.TemplateFuncsPolicy is useless without this specific
// adaptor. We also know that it is not a good idea to have two
// policies with the same function or we can? wait. No we can't.
// We can't because:
// - the RenderPolicy should accept any type of render process, not only tempaltes.
// - I didn't design iris/policy.go to keep logic about implementation, this would make that very limited
// and I don't want to break that just for the templates.
// - We want community to be able to create packages which can adapt template functions but
// not to worry about the rest of the template engine adaptor policy.
// And even don't worry if the user has registered or use a template engine at all.
// So we should keep separate the TemplateFuncsPolicy(just map[string]interface{})
// from the rest of the implementation.
//
// So when the community wants to create a template adaptor has two options:
// - Use the RenderPolicy which is just a func
// - Use the kataras/iris/adaptors/view.Adaptor adaptor wrapper for RenderPolicy with a compination of kataras/go-template/.Engine
//
//
// So here is the only place we adapt the iris.TemplateFuncsPolicy to the tempaltes, if and only if templates are used,
// otherwise they are just ignored without any creepy things.
//
// The TemplateFuncsPolicy will work in compination with the specific template adaptor's functions(see html.go and the rest)
if len(frame.TemplateFuncsPolicy) > 0 {
mux.SetFuncMapToEngine(frame.TemplateFuncsPolicy, h.engine)
}
if err := mux.Load(); err != nil {
s.Log(iris.ProdMode, err.Error())
}
},
}
// adapt the build event to the main policies
evt.Adapt(frame)
r := iris.RenderPolicy(func(out io.Writer, file string, tmplContext interface{}, options ...map[string]interface{}) (error, bool) {
// template mux covers that but maybe we have more than one RenderPolicy
// and each of them carries a different mux on the new design.
if strings.Contains(file, h.extension) {
return mux.ExecuteWriter(out, file, tmplContext, options...), true
}
return nil, false
})
r.Adapt(frame)
}

48
adaptors/view/amber.go Normal file
View File

@@ -0,0 +1,48 @@
package view
import (
"github.com/kataras/go-template/amber"
)
// AmberAdaptor is the adaptor for the Amber, simple, engine.
// Read more about the Amber Go Template at:
// https://github.com/eknkc/amber
// and https://github.com/kataras/go-template/tree/master/amber
type AmberAdaptor struct {
*Adaptor
engine *amber.Engine
}
// Amber returns a new kataras/go-template/amber template engine
// with the same features as all iris' view engines have:
// Binary assets load (templates inside your executable with .go extension)
// Layout, Funcs, {{ url }} {{ urlpath}} for reverse routing and much more.
//
// Read more: https://github.com/eknkc/amber
func Amber(directory string, extension string) *AmberAdaptor {
e := amber.New()
return &AmberAdaptor{
Adaptor: NewAdaptor(directory, extension, e),
engine: e,
}
}
// Funcs adds the elements of the argument map to the template's function map.
// It is legal to overwrite elements of the default actions:
// - url func(routeName string, args ...string) string
// - urlpath func(routeName string, args ...string) string
// - render func(fullPartialName string) (template.HTML, error).
func (a *AmberAdaptor) Funcs(funcMap map[string]interface{}) *AmberAdaptor {
if len(funcMap) == 0 {
return a
}
// configuration maps are never nil, because
// they are initialized at each of the engine's New func
// so we're just passing them inside it.
for k, v := range funcMap {
a.engine.Config.Funcs[k] = v
}
return a
}

93
adaptors/view/django.go Normal file
View File

@@ -0,0 +1,93 @@
package view
import (
"github.com/kataras/go-template/django"
)
type (
// Value conversion for django.Value
Value django.Value
// Error conversion for django.Error
Error django.Error
// FilterFunction conversion for django.FilterFunction
FilterFunction func(in *Value, param *Value) (out *Value, err *Error)
)
// this exists because of moving the pongo2 to the vendors without conflictitions if users
// wants to register pongo2 filters they can use this django.FilterFunc to do so.
func convertFilters(djangoFilters map[string]FilterFunction) map[string]django.FilterFunction {
filters := make(map[string]django.FilterFunction, len(djangoFilters))
for k, v := range djangoFilters {
func(filterName string, filterFunc FilterFunction) {
fn := django.FilterFunction(func(in *django.Value, param *django.Value) (*django.Value, *django.Error) {
theOut, theErr := filterFunc((*Value)(in), (*Value)(param))
return (*django.Value)(theOut), (*django.Error)(theErr)
})
filters[filterName] = fn
}(k, v)
}
return filters
}
// DjangoAdaptor is the adaptor for the Django engine.
// Read more about the Django Go Template at:
// https://github.com/flosch/pongo2
// and https://github.com/kataras/go-template/tree/master/django
type DjangoAdaptor struct {
*Adaptor
engine *django.Engine
filters map[string]FilterFunction
}
// Django returns a new kataras/go-template/django template engine
// with the same features as all iris' view engines have:
// Binary assets load (templates inside your executable with .go extension)
// Layout, Funcs, {{ url }} {{ urlpath}} for reverse routing and much more.
//
// Read more: https://github.com/flosch/pongo2
func Django(directory string, extension string) *DjangoAdaptor {
e := django.New()
return &DjangoAdaptor{
Adaptor: NewAdaptor(directory, extension, e),
engine: e,
filters: make(map[string]FilterFunction, 0),
}
}
// Filters for pongo2, map[name of the filter] the filter function .
//
// Note, these Filters function overrides ALL the previous filters
// It SETS a new filter map based on the given 'filtersMap' parameter.
func (d *DjangoAdaptor) Filters(filtersMap map[string]FilterFunction) *DjangoAdaptor {
if len(filtersMap) == 0 {
return d
}
// configuration maps are never nil, because
// they are initialized at each of the engine's New func
// so we're just passing them inside it.
filters := convertFilters(filtersMap)
d.engine.Config.Filters = filters
return d
}
// Globals share context fields between templates. https://github.com/flosch/pongo2/issues/35
func (d *DjangoAdaptor) Globals(globalsMap map[string]interface{}) *DjangoAdaptor {
if len(globalsMap) == 0 {
return d
}
for k, v := range globalsMap {
d.engine.Config.Globals[k] = v
}
return d
}
// DebugTemplates enables template debugging.
// The verbose error messages will appear in browser instead of quiet passes with error code.
func (d *DjangoAdaptor) DebugTemplates(debug bool) *DjangoAdaptor {
d.engine.Config.DebugTemplates = debug
return d
}

View File

@@ -0,0 +1,62 @@
package view
import (
"github.com/kataras/go-template/handlebars"
)
// HandlebarsAdaptor is the adaptor for the Handlebars engine.
// Read more about the Handlebars Go Template at:
// https://github.com/aymerick/raymond
// and https://github.com/kataras/go-template/tree/master/handlebars
type HandlebarsAdaptor struct {
*Adaptor
engine *handlebars.Engine
}
// Handlebars returns a new kataras/go-template/handlebars template engine
// with the same features as all iris' view engines have:
// Binary assets load (templates inside your executable with .go extension)
// Layout, Funcs, {{ url }} {{ urlpath}} for reverse routing and much more.
//
// Read more: https://github.com/aymerick/raymond
func Handlebars(directory string, extension string) *HandlebarsAdaptor {
e := handlebars.New()
return &HandlebarsAdaptor{
Adaptor: NewAdaptor(directory, extension, e),
engine: e,
}
}
// Layout sets the layout template file which inside should use
// the {{ yield }} func to yield the main template file
// and optionally {{partial/partial_r/render}} to render other template files like headers and footers
//
// The 'tmplLayoutFile' is a relative path of the templates base directory,
// for the template file whith its extension.
//
// Example: Handlebars("./templates", ".html").Layout("layouts/mainLayout.html")
// // mainLayout.html is inside: "./templates/layouts/".
//
// Note: Layout can be changed for a specific call
// action with the option: "layout" on the Iris' context.Render function.
func (h *HandlebarsAdaptor) Layout(tmplLayoutFile string) *HandlebarsAdaptor {
h.engine.Config.Layout = tmplLayoutFile
return h
}
// Funcs adds the elements of the argument map to the template's function map.
// It is legal to overwrite elements of the default actions:
// - url func(routeName string, args ...string) string
// - urlpath func(routeName string, args ...string) string
// - and handlebars specific, read more: https://github.com/aymerick/raymond.
func (h *HandlebarsAdaptor) Funcs(funcMap map[string]interface{}) *HandlebarsAdaptor {
if funcMap == nil {
return h
}
for k, v := range funcMap {
h.engine.Config.Helpers[k] = v
}
return h
}

92
adaptors/view/html.go Normal file
View File

@@ -0,0 +1,92 @@
package view
import (
"github.com/kataras/go-template/html"
)
// HTMLAdaptor is the html engine policy adaptor
// used like the "html/template" standard go package
// but with a lot of extra features by.
//
// This is just a wrapper of kataras/go-template/html.
type HTMLAdaptor struct {
*Adaptor
engine *html.Engine
}
// HTML creates and returns a new kataras/go-template/html engine.
// The html engine used like the "html/template" standard go package
// but with a lot of extra features.
func HTML(directory string, extension string) *HTMLAdaptor {
engine := html.New()
return &HTMLAdaptor{
Adaptor: NewAdaptor(directory, extension, engine),
// create the underline engine with the default configuration,
// which can be changed by this type.
//The whole funcs should called only before Iris' build & listen state.
engine: engine, // we need that for the configuration only.
}
}
// Delims sets the action delimiters to the specified strings, to be used in
// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
// definitions will inherit the settings. An empty delimiter stands for the
// corresponding default: {{ or }}.
func (h *HTMLAdaptor) Delims(left string, right string) *HTMLAdaptor {
if left != "" && right != "" {
h.engine.Config.Left = left
h.engine.Config.Right = right
}
return h
}
// Layout sets the layout template file which inside should use
// the {{ yield }} func to yield the main template file
// and optionally {{partial/partial_r/render}} to render other template files like headers and footers
//
// The 'tmplLayoutFile' is a relative path of the templates base directory,
// for the template file whith its extension.
//
// Example: HTML("./templates", ".html").Layout("layouts/mainLayout.html")
// // mainLayout.html is inside: "./templates/layouts/".
//
// Note: Layout can be changed for a specific call
// action with the option: "layout" on the Iris' context.Render function.
func (h *HTMLAdaptor) Layout(tmplLayoutFile string) *HTMLAdaptor {
h.engine.Config.Layout = tmplLayoutFile
return h
}
// LayoutFuncs adds the elements of the argument map to the template's layout-only function map.
// It is legal to overwrite elements of the default layout actions:
// - yield func() (template.HTML, error)
// - current func() (string, error)
// - partial func(partialName string) (template.HTML, error)
// - partial_r func(partialName string) (template.HTML, error)
// - render func(fullPartialName string) (template.HTML, error).
func (h *HTMLAdaptor) LayoutFuncs(funcMap map[string]interface{}) *HTMLAdaptor {
if funcMap != nil {
h.engine.Config.LayoutFuncs = funcMap
}
return h
}
// Funcs adds the elements of the argument map to the template's function map.
// It is legal to overwrite elements of the default actions:
// - url func(routeName string, args ...string) string
// - urlpath func(routeName string, args ...string) string
// - render func(fullPartialName string) (template.HTML, error).
func (h *HTMLAdaptor) Funcs(funcMap map[string]interface{}) *HTMLAdaptor {
if len(funcMap) == 0 {
return h
}
// configuration maps are never nil, because
// they are initialized at each of the engine's New func
// so we're just passing them inside it.
for k, v := range funcMap {
h.engine.Config.Funcs[k] = v
}
return h
}

21
adaptors/view/pug.go Normal file
View File

@@ -0,0 +1,21 @@
package view
import (
"github.com/Joker/jade"
)
// Pug (or Jade) returns a new kataras/go-template/pug engine.
// It shares the same exactly logic with the
// HTMLAdaptor, it uses the same exactly configuration.
// It has got some features and a lot of functions
// which will make your life easier.
// Read more about the Jade Go Template: https://github.com/Joker/jade
func Pug(directory string, extension string) *HTMLAdaptor {
h := HTML(directory, extension)
// because I has designed the kataras/go-template
// so powerful, we can just pass a parser middleware
// into the standard html template engine
// and we're ready to go.
h.engine.Middleware = jade.Parse
return h
}