Update to v4.0.0-alpha.1

This commit is contained in:
Makis Maropoulos 2016-07-13 06:28:09 +03:00
parent 5cfe19c793
commit b99afb2875
27 changed files with 447 additions and 1927 deletions

View File

@ -2,6 +2,26 @@
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras/iris` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`. **How to upgrade**: remove your `$GOPATH/src/github.com/kataras/iris` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`.
## 3.0.0 -> 4.0.0-alpha.1
[logger](https://github.com/iris-contrib/logger), [rest](https://github.com/iris-contrib/rest) and all [template engines](https://github.com/iris-contrib/template) **moved** to the [iris-contrib](https://github.com/iris-contrib).
- `config.Logger` -> `iris.Logger.Config`
- `config.Render/config.Render.Rest/config.Render.Template` -> **Removed**
- `config.Render.Rest` -> `rest.Config`
- `config.Render.Template` -> `$TEMPLATE_ENGINE.Config` except Directory,Extensions, Assets, AssetNames,
- `config.Render.Template.Directory` -> `iris.UseEngine($TEMPLAET_ENGINE.New()).Directory("./templates", ".html")`
- `config.Render.Template.Assets` -> `iris.UseEngine($TEMPLAET_ENGINE.New()).Directory("./templates",".html").Binary(assetFn func(name string) ([]byte, error), namesFn func() []string)`
- `context.ExecuteTemplate` -> **Removed**, you can use the `context.Response.BodyWriter()` to get its writer and execute html/template engine manually, but this is useless because we support the best support for template engines among all other (golang) web frameworks
- **Added** `config.Server.ReadBufferSize & config.Server.WriteBufferSize` which can be passed as configuration fields inside `iris.ListenTo(config.Server{...})`, which does the same job as `iris.Listen`
- **Added** `iris.UseEngine($TEMPLAET_ENGINE.New()).Directory("./templates", ".html")` to register a template engine, now iris supports multi template engines, each template engine has its own file extension, no big changes on context.Render except the last parameter:
- `context.Render(filename string, binding interface{}, layout string{})` -> `context.Render(filename string, binding interface{}, options ...map[string]interface{}) | context.Render("myfile.html", myPage{}, iris.Map{"gzip":true,"layout":"layouts/MyLayout.html"}) |`
E-book and examples are not yet updated, no big changes.
## 3.0.0-rc.4 -> 3.0.0-pre.release ## 3.0.0-rc.4 -> 3.0.0-pre.release
- `context.PostFormValue` -> `context.FormValueString`, old func stays until the next revision - `context.PostFormValue` -> `context.FormValueString`, old func stays until the next revision

View File

@ -2,10 +2,10 @@
[![Travis Widget]][Travis] [![Release Widget]][Release] [![Report Widget]][Report] [![License Widget]][License] [![Chat Widget]][Chat] [![Documentation Widget]][Documentation] [![Travis Widget]][Travis] [![Release Widget]][Release] [![Report Widget]][Report] [![License Widget]][License] [![Chat Widget]][Chat] [![Documentation Widget]][Documentation]
The [fastest](#benchmarks) web framework for Go. The fastest web framework for Go.
[![Alt: The results have been updated on July 1, 2016](https://raw.githubusercontent.com/smallnest/go-web-framework-benchmark/master/benchmark.png)](https://github.com/smallnest/go-web-framework-benchmark) [![Alt: Benchmark results with pipeline and 500ms processing time](https://raw.githubusercontent.com/iris-contrib/website/gh-pages/assets/benchmark_horizontal_transparent.png)](#benchmarks)
@ -22,7 +22,7 @@ func main() {
c.JSON(iris.StatusOK, iris.Map{ c.JSON(iris.StatusOK, iris.Map{
"Name": "Iris", "Name": "Iris",
"Born": "13 March 2016", "Born": "13 March 2016",
"Stars": 3693, "Stars": 4262,
}) })
}) })
iris.Listen(":8080") iris.Listen(":8080")
@ -46,7 +46,7 @@ The only requirement is the [Go Programming Language](https://golang.org/dl), at
$ go get -u github.com/kataras/iris/iris $ go get -u github.com/kataras/iris/iris
``` ```
>If you have installation issues and you are connected to the Internet through China please, [click here](https://kataras.gitbooks.io/iris/content/install.html). >If you have installation issues or you are connected to the Internet through China please, [click here](https://kataras.gitbooks.io/iris/content/install.html).
FAQ FAQ
------------ ------------
@ -76,6 +76,12 @@ Features
| Name | Description | Usage | | Name | Description | Usage |
| ------------------|:---------------------:|-------:| | ------------------|:---------------------:|-------:|
| [HTML/Default Engine ](https://github.com/iris-contrib/template/tree/master/html) | HTML Template Engine (Default) |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_html_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
| [Django Engine ](https://github.com/iris-contrib/template/tree/master/django) | Django Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_django_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/brender_templates.html)
| [Pug/Jade Engine ](https://github.com/iris-contrib/template/tree/master/pug) | Pug Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_pug_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
| [Handlebars Engine ](https://github.com/iris-contrib/template/tree/master/handlebars) | Handlebars Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_handlebars_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
| [Amber Engine ](https://github.com/iris-contrib/template/tree/master/amber) | Amber Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_amber_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
| [Markdown Engine ](https://github.com/iris-contrib/template/tree/master/markdown) | Markdown Template Engine |[example 1](https://github.com/iris-contrib/examples/blob/master/template_engines/template_markdown_1/main.go), [book section](https://kataras.gitbooks.io/iris/content/render_templates.html)
| [Basicauth Middleware ](https://github.com/iris-contrib/middleware/tree/master/basicauth) | HTTP Basic authentication |[example 1](https://github.com/iris-contrib/examples/blob/master/middleware_basicauth_1/main.go), [example 2](https://github.com/iris-contrib/examples/blob/master/middleware_basicauth_2/main.go), [book section](https://kataras.gitbooks.io/iris/content/basic-authentication.html) | | [Basicauth Middleware ](https://github.com/iris-contrib/middleware/tree/master/basicauth) | HTTP Basic authentication |[example 1](https://github.com/iris-contrib/examples/blob/master/middleware_basicauth_1/main.go), [example 2](https://github.com/iris-contrib/examples/blob/master/middleware_basicauth_2/main.go), [book section](https://kataras.gitbooks.io/iris/content/basic-authentication.html) |
| [JWT Middleware ](https://github.com/iris-contrib/middleware/tree/master/jwt) | JSON Web Tokens |[example ](https://github.com/iris-contrib/examples/blob/master/middleware_jwt/main.go), [book section](https://kataras.gitbooks.io/iris/content/jwt.html) | | [JWT Middleware ](https://github.com/iris-contrib/middleware/tree/master/jwt) | JSON Web Tokens |[example ](https://github.com/iris-contrib/examples/blob/master/middleware_jwt/main.go), [book section](https://kataras.gitbooks.io/iris/content/jwt.html) |
| [Cors Middleware ](https://github.com/iris-contrib/middleware/tree/master/cors) | Cross Origin Resource Sharing W3 specification | [how to use ](https://github.com/iris-contrib/middleware/tree/master/cors#how-to-use) | | [Cors Middleware ](https://github.com/iris-contrib/middleware/tree/master/cors) | Cross Origin Resource Sharing W3 specification | [how to use ](https://github.com/iris-contrib/middleware/tree/master/cors#how-to-use) |
@ -133,30 +139,27 @@ Iris does not force you to use any specific ORM or template engine. With support
Testing Testing
------------ ------------
Tests are located to the [iris-contrib/tests repository](https://github.com/iris-contrib/tests), community should write some code there! Community should write third-party or iris base tests to the [iris-contrib/tests repository](https://github.com/iris-contrib/tests).
I recommend writing your API tests using this new library, [httpexpect](https://github.com/gavv/httpexpect) which supports Iris and fasthttp now, after my request [here](https://github.com/gavv/httpexpect/issues/2). I recommend writing your API tests using this new library, [httpexpect](https://github.com/gavv/httpexpect) which supports Iris and fasthttp now, after my request [here](https://github.com/gavv/httpexpect/issues/2).
Versioning Versioning
------------ ------------
Current: **v3.0.0** Current: **v4.0.0-alpha.1**
> Iris is an active project > Iris is an active project
Read more about Semantic Versioning 2.0.0
- http://semver.org/
- https://en.wikipedia.org/wiki/Software_versioning
- https://wiki.debian.org/UpstreamGuide#Releases_and_Versions
Todo Todo
------------ ------------
> for the next version 'v4' > for the next version 'v4'
- [ ] Refactor & extend view engine, separate the engines from the main code base, easier for the community to create new view engines. - [x] Refactor & extend view engine, separate the engines from the main code base, easier for the community to create new view engines
- [ ] Create a router as optional plugin, for optional path parts. Its name, 'ryan', taken from the community-member and donator who requested this feature. - [ ] Implement all [opened community's feature requests](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aopen+label%3A%22feature+request%22)
- [ ] Extend i18n middleware for easier and better internalization support
- [ ] Create a router as optional plugin, for optional path parts. Its name, 'ryan', taken from the community-member and donator who requested this feature
- [ ] Extend the iris control plugin - [ ] Extend the iris control plugin
- [ ] Remove deprecated functions. - [ ] Remove deprecated functions
- [ ] Will think more :)
> completed for release 'v3' > completed for release 'v3'
@ -191,7 +194,7 @@ License can be found [here](LICENSE).
[Travis]: http://travis-ci.org/kataras/iris [Travis]: http://travis-ci.org/kataras/iris
[License Widget]: https://img.shields.io/badge/license-MIT%20%20License%20-E91E63.svg?style=flat-square [License Widget]: https://img.shields.io/badge/license-MIT%20%20License%20-E91E63.svg?style=flat-square
[License]: https://github.com/kataras/iris/blob/master/LICENSE [License]: https://github.com/kataras/iris/blob/master/LICENSE
[Release Widget]: https://img.shields.io/badge/release-v3.0.0-blue.svg?style=flat-square [Release Widget]: https://img.shields.io/badge/release-v4.0.0--alpha.1-blue.svg?style=flat-square
[Release]: https://github.com/kataras/iris/releases [Release]: https://github.com/kataras/iris/releases
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square [Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
[Chat]: https://kataras.rocket.chat/channel/iris [Chat]: https://kataras.rocket.chat/channel/iris

View File

@ -6,6 +6,13 @@ import (
) )
var ( var (
// Charset character encoding for template rendering
Charset = "UTF-8"
)
var (
// TimeFormat default time format for any kind of datetime parsing
TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
// StaticCacheDuration expiration duration for INACTIVE file handlers // StaticCacheDuration expiration duration for INACTIVE file handlers
StaticCacheDuration = 20 * time.Second StaticCacheDuration = 20 * time.Second
// CompressedFileSuffix is the suffix to add to the name of // CompressedFileSuffix is the suffix to add to the name of
@ -13,4 +20,14 @@ var (
// //
// Defaults to iris-fasthttp.gz // Defaults to iris-fasthttp.gz
CompressedFileSuffix = "iris-fasthttp.gz" CompressedFileSuffix = "iris-fasthttp.gz"
// ContentTypeHTML defaults to text/html but you can change it, changes the template's content type also
ContentTypeHTML = "text/html"
)
const (
// NoLayout to disable layout for a particular template file
NoLayout = "@.|.@iris_no_layout@.|.@"
// TemplateLayoutContextKey is the name of the user values which can be used to set a template layout from a middleware and override the parent's
TemplateLayoutContextKey = "templateLayout"
) )

View File

@ -1,6 +1,9 @@
package config package config
import "github.com/imdario/mergo" import (
"github.com/imdario/mergo"
"github.com/iris-contrib/rest"
)
// Default values for base Iris conf // Default values for base Iris conf
const ( const (
@ -78,16 +81,19 @@ type (
// http://debug.yourdomain:PORT/threadcreate // http://debug.yourdomain:PORT/threadcreate
// http://debug.yourdomain:PORT/pprof/block // http://debug.yourdomain:PORT/pprof/block
ProfilePath string ProfilePath string
// DisableTemplateEngines set to true to disable loading the default template engine (html/template) and disallow the use of iris.UseEngine
// Logger the configuration for the logger // default is false
// Iris logs ONLY SEMANTIC errors and the banner if enabled DisableTemplateEngines bool
Logger Logger // IsDevelopment iris will act like a developer, for example
// If true then re-builds the templates on each request
// default is false
IsDevelopment bool
// Sessions contains the configs for sessions // Sessions contains the configs for sessions
Sessions Sessions Sessions Sessions
// Render contains the configs for template and rest configuration // Rest contains the configs for rest render configuration
Render Render Rest rest.Config
// Websocket contains the configs for Websocket's server integration // Websocket contains the configs for Websocket's server integration
Websocket *Websocket Websocket *Websocket
@ -95,41 +101,21 @@ type (
// Tester contains the configs for the test framework, so far we have only one because all test framework's configs are setted by the iris itself // Tester contains the configs for the test framework, so far we have only one because all test framework's configs are setted by the iris itself
Tester Tester Tester Tester
} }
// Render struct keeps organise all configuration about rendering, templates and rest currently.
Render struct {
// Template the configs for template
Template Template
// Rest configs for rendering.
//
// these options inside this config don't have any relation with the TemplateEngine
// from github.com/kataras/iris/rest
Rest Rest
}
) )
// DefaultRender returns default configuration for templates and rest rendering
func DefaultRender() Render {
return Render{
// set the default template config both not nil and default Engine to Standar
Template: DefaultTemplate(),
// set the default configs for rest
Rest: DefaultRest(),
}
}
// Default returns the default configuration for the Iris staton // Default returns the default configuration for the Iris staton
func Default() Iris { func Default() Iris {
return Iris{ return Iris{
DisablePathCorrection: DefaultDisablePathCorrection, DisablePathCorrection: DefaultDisablePathCorrection,
DisablePathEscape: DefaultDisablePathEscape, DisablePathEscape: DefaultDisablePathEscape,
DisableBanner: false, DisableBanner: false,
ProfilePath: "", DisableTemplateEngines: false,
Logger: DefaultLogger(), IsDevelopment: false,
Sessions: DefaultSessions(), ProfilePath: "",
Render: DefaultRender(), Sessions: DefaultSessions(),
Websocket: DefaultWebsocket(), Rest: rest.DefaultConfig(),
Tester: DefaultTester(), Websocket: DefaultWebsocket(),
Tester: DefaultTester(),
} }
} }
@ -158,18 +144,3 @@ func (c Iris) MergeSingle(cfg Iris) (config Iris) {
return return
} }
/* maybe some day
// FromFile returns the configuration for Iris station
//
// receives one parameter
// pathIni(string) the file path of the configuration-ini style
//
// returns an error if something bad happens
func FromFile(pathIni string) (c Iris, err error) {
c = Iris{}
err = ini.MapTo(&c, pathIni)
return
}
*/

View File

@ -69,6 +69,7 @@ type Server struct {
// //
// Default buffer size is used if not set. // Default buffer size is used if not set.
WriteBufferSize int WriteBufferSize int
// RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT) // RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT)
// //
// NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server // NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server

View File

@ -10,7 +10,6 @@ import (
"encoding/json" "encoding/json"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"html/template"
"io" "io"
"net" "net"
"os" "os"
@ -476,23 +475,23 @@ func (ctx *Context) Data(status int, v []byte) error {
// RenderWithStatus builds up the response from the specified template and bindings. // RenderWithStatus builds up the response from the specified template and bindings.
// Note: parameter layout has meaning only when using the iris.HTMLTemplate // Note: parameter layout has meaning only when using the iris.HTMLTemplate
func (ctx *Context) RenderWithStatus(status int, name string, binding interface{}, layout ...string) error { func (ctx *Context) RenderWithStatus(status int, name string, binding interface{}, options ...map[string]interface{}) error {
ctx.SetStatusCode(status) ctx.SetStatusCode(status)
return ctx.framework.templates.Render(ctx, name, binding, layout...) return ctx.framework.templates.GetBy(name).Execute(ctx, name, binding, options...)
} }
// Render same as .RenderWithStatus but with status to iris.StatusOK (200) // Render same as .RenderWithStatus but with status to iris.StatusOK (200)
func (ctx *Context) Render(name string, binding interface{}, layout ...string) error { func (ctx *Context) Render(name string, binding interface{}, options ...map[string]interface{}) error {
errCode := ctx.RequestCtx.Response.StatusCode() errCode := ctx.RequestCtx.Response.StatusCode()
if errCode <= 0 { if errCode <= 0 {
errCode = StatusOK errCode = StatusOK
} }
return ctx.RenderWithStatus(errCode, name, binding, layout...) return ctx.RenderWithStatus(errCode, name, binding, options...)
} }
// MustRender same as .Render but returns 500 internal server http status (error) if rendering fail // MustRender same as .Render but returns 500 internal server http status (error) if rendering fail
func (ctx *Context) MustRender(name string, binding interface{}, layout ...string) { func (ctx *Context) MustRender(name string, binding interface{}, options ...map[string]interface{}) {
if err := ctx.Render(name, binding, layout...); err != nil { if err := ctx.Render(name, binding, options...); err != nil {
ctx.Panic() ctx.Panic()
ctx.framework.Logger.Dangerf("MustRender panics for client with IP: %s On template: %s.Trace: %s\n", ctx.RemoteAddr(), name, err) ctx.framework.Logger.Dangerf("MustRender panics for client with IP: %s On template: %s.Trace: %s\n", ctx.RemoteAddr(), name, err)
} }
@ -500,8 +499,8 @@ func (ctx *Context) MustRender(name string, binding interface{}, layout ...strin
// TemplateString accepts a template filename, its context data and returns the result of the parsed template (string) // TemplateString accepts a template filename, its context data and returns the result of the parsed template (string)
// if any error returns empty string // if any error returns empty string
func (ctx *Context) TemplateString(name string, binding interface{}, layout ...string) string { func (ctx *Context) TemplateString(name string, binding interface{}, options ...map[string]interface{}) string {
return ctx.framework.TemplateString(name, binding, layout...) return ctx.framework.TemplateString(name, binding, options...)
} }
// JSON marshals the given interface object and writes the JSON response. // JSON marshals the given interface object and writes the JSON response.
@ -537,17 +536,6 @@ func (ctx *Context) Markdown(status int, markdown string) {
ctx.HTML(status, ctx.MarkdownString(markdown)) ctx.HTML(status, ctx.MarkdownString(markdown))
} }
// ExecuteTemplate executes a simple html template, you can use that if you already have the cached templates
// the recommended way to render is to use iris.Templates("./templates/path/*.html") and ctx.RenderFile("filename.html",struct{})
// accepts 2 parameters
// the first parameter is the template (*template.Template)
// the second parameter is the page context (interfac{})
// returns an error if any errors occurs while executing this template
func (ctx *Context) ExecuteTemplate(tmpl *template.Template, pageContext interface{}) error {
ctx.RequestCtx.SetContentType(contentHTML + ctx.framework.rest.CompiledCharset)
return errTemplateExecute.With(tmpl.Execute(ctx.RequestCtx.Response.BodyWriter(), pageContext))
}
// ServeContent serves content, headers are autoset // ServeContent serves content, headers are autoset
// receives three parameters, it's low-level function, instead you can use .ServeFile(string) // receives three parameters, it's low-level function, instead you can use .ServeFile(string)
// //

View File

@ -2,7 +2,6 @@ package context
import ( import (
"bufio" "bufio"
"html/template"
"io" "io"
"time" "time"
@ -46,17 +45,16 @@ type (
Write(string, ...interface{}) Write(string, ...interface{})
HTML(int, string) HTML(int, string)
Data(int, []byte) error Data(int, []byte) error
RenderWithStatus(int, string, interface{}, ...string) error RenderWithStatus(int, string, interface{}, ...map[string]interface{}) error
Render(string, interface{}, ...string) error Render(string, interface{}, ...map[string]interface{}) error
MustRender(string, interface{}, ...string) MustRender(string, interface{}, ...map[string]interface{})
TemplateString(string, interface{}, ...string) string TemplateString(string, interface{}, ...map[string]interface{}) string
MarkdownString(string) string MarkdownString(string) string
Markdown(int, string) Markdown(int, string)
JSON(int, interface{}) error JSON(int, interface{}) error
JSONP(int, string, interface{}) error JSONP(int, string, interface{}) error
Text(int, string) error Text(int, string) error
XML(int, interface{}) error XML(int, interface{}) error
ExecuteTemplate(*template.Template, interface{}) error
ServeContent(io.ReadSeeker, string, time.Time, bool) error ServeContent(io.ReadSeeker, string, time.Time, bool) error
ServeFile(string, bool) error ServeFile(string, bool) error
SendFile(string, string) error SendFile(string, string) error

View File

@ -89,9 +89,7 @@ func MustUseFunc(handlersFn ...HandlerFunc) {
// Use it when you want to add a global middleware to all parties, to all routes in all subdomains // Use it when you want to add a global middleware to all parties, to all routes in all subdomains
// It can be called after other, (but before .Listen of course) // It can be called after other, (but before .Listen of course)
func (s *Framework) MustUse(handlers ...Handler) { func (s *Framework) MustUse(handlers ...Handler) {
for _, r := range s.mux.lookups { s.UseGlobal(handlers...)
r.middleware = append(handlers, r.middleware...)
}
} }
// MustUseFunc registers HandlerFunc middleware to the beginning, prepends them instead of append // MustUseFunc registers HandlerFunc middleware to the beginning, prepends them instead of append
@ -99,7 +97,7 @@ func (s *Framework) MustUse(handlers ...Handler) {
// Use it when you want to add a global middleware to all parties, to all routes in all subdomains // Use it when you want to add a global middleware to all parties, to all routes in all subdomains
// It can be called after other, (but before .Listen of course) // It can be called after other, (but before .Listen of course)
func (s *Framework) MustUseFunc(handlersFn ...HandlerFunc) { func (s *Framework) MustUseFunc(handlersFn ...HandlerFunc) {
s.MustUse(convertToHandlers(handlersFn)...) s.UseGlobalFunc(handlersFn...)
} }
// PostFormMulti returns a slice of string from post request's data // PostFormMulti returns a slice of string from post request's data

2
glide.lock generated
View File

@ -101,7 +101,7 @@ imports:
subpackages: subpackages:
- github.com\valyala\bytebufferpool - github.com\valyala\bytebufferpool
- name: github.com/valyala/fasthttp - name: github.com/valyala/fasthttp
version: 97d96cb3b7feaf22ce9b813313cd8ae20b3589b9 version: 886e5411604884629c566961ea8ed2cec074e4b1
subpackages: subpackages:
- github.com\valyala\fasthttp - github.com\valyala\fasthttp
- github.com\valyala\fasthttp\fasthttpadaptor - github.com\valyala\fasthttp\fasthttpadaptor

11
http.go
View File

@ -13,8 +13,8 @@ import (
"time" "time"
"github.com/iris-contrib/errors" "github.com/iris-contrib/errors"
"github.com/iris-contrib/logger"
"github.com/kataras/iris/config" "github.com/kataras/iris/config"
"github.com/kataras/iris/logger"
"github.com/kataras/iris/utils" "github.com/kataras/iris/utils"
"github.com/valyala/fasthttp" "github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttpadaptor" "github.com/valyala/fasthttp/fasthttpadaptor"
@ -262,6 +262,11 @@ func newServer(cfg config.Server) *Server {
// prepare just prepares the listening addr // prepare just prepares the listening addr
func (s *Server) prepare() { func (s *Server) prepare() {
s.Config.ListeningAddr = config.ServerParseAddr(s.Config.ListeningAddr) s.Config.ListeningAddr = config.ServerParseAddr(s.Config.ListeningAddr)
if s.Server != nil {
s.Server.MaxRequestBodySize = s.Config.MaxRequestBodySize
s.Server.ReadBufferSize = s.Config.ReadBufferSize
s.Server.WriteBufferSize = s.Config.WriteBufferSize
}
} }
// IsListening returns true if server is listening/started, otherwise false // IsListening returns true if server is listening/started, otherwise false
@ -399,10 +404,6 @@ func (s *Server) Open(h fasthttp.RequestHandler) error {
s.prepare() // do it again for any case s.prepare() // do it again for any case
s.Server.MaxRequestBodySize = s.Config.MaxRequestBodySize
s.Server.ReadBufferSize = s.Config.ReadBufferSize
s.Server.WriteBufferSize = s.Config.WriteBufferSize
if s.Config.RedirectTo != "" { if s.Config.RedirectTo != "" {
// override the handler and redirect all requests to this addr // override the handler and redirect all requests to this addr
s.Server.Handler = func(reqCtx *fasthttp.RequestCtx) { s.Server.Handler = func(reqCtx *fasthttp.RequestCtx) {

122
iris.go
View File

@ -65,11 +65,11 @@ import (
"github.com/gavv/httpexpect" "github.com/gavv/httpexpect"
"github.com/iris-contrib/errors" "github.com/iris-contrib/errors"
"github.com/iris-contrib/logger"
"github.com/iris-contrib/rest"
"github.com/iris-contrib/template/html"
"github.com/kataras/iris/config" "github.com/kataras/iris/config"
"github.com/kataras/iris/context" "github.com/kataras/iris/context"
"github.com/kataras/iris/logger"
"github.com/kataras/iris/render/rest"
"github.com/kataras/iris/render/template"
"github.com/kataras/iris/sessions" "github.com/kataras/iris/sessions"
"github.com/kataras/iris/utils" "github.com/kataras/iris/utils"
"github.com/kataras/iris/websocket" "github.com/kataras/iris/websocket"
@ -81,27 +81,7 @@ import (
const ( const (
// Version of the iris // Version of the iris
Version = "3.0.0" Version = "4.0.0-alpha.1"
// HTMLEngine conversion for config.HTMLEngine
HTMLEngine = config.HTMLEngine
// PongoEngine conversion for config.PongoEngine
PongoEngine = config.PongoEngine
// MarkdownEngine conversion for config.MarkdownEngine
MarkdownEngine = config.MarkdownEngine
// JadeEngine conversion for config.JadeEngine
JadeEngine = config.JadeEngine
// AmberEngine conversion for config.AmberEngine
AmberEngine = config.AmberEngine
// HandlebarsEngine conversion for config.HandlebarsEngine
HandlebarsEngine = config.HandlebarsEngine
// DefaultEngine conversion for config.DefaultEngine
DefaultEngine = config.DefaultEngine
// NoEngine conversion for config.NoEngine
NoEngine = config.NoEngine
// NoLayout to disable layout for a particular template file
// conversion for config.NoLayout
NoLayout = config.NoLayout
banner = ` _____ _ banner = ` _____ _
|_ _| (_) |_ _| (_)
@ -177,7 +157,7 @@ type (
Lookups() []Route Lookups() []Route
Path(string, ...interface{}) string Path(string, ...interface{}) string
URL(string, ...interface{}) string URL(string, ...interface{}) string
TemplateString(string, interface{}, ...string) string TemplateString(string, interface{}, ...map[string]interface{}) string
Tester(t *testing.T) *httpexpect.Expect Tester(t *testing.T) *httpexpect.Expect
} }
@ -187,12 +167,14 @@ type (
Framework struct { Framework struct {
*muxAPI *muxAPI
rest *rest.Render rest *rest.Render
templates *template.Template
sessions *sessions.Manager sessions *sessions.Manager
templates *TemplateEngines
// fields which are useful to the user/dev // fields which are useful to the user/dev
// the last added server is the main server // the last added server is the main server
Servers *ServerList Servers *ServerList
Config *config.Iris Config *config.Iris
// configuration by instance.Logger.Config
Logger *logger.Logger Logger *logger.Logger
Plugins PluginContainer Plugins PluginContainer
Websocket websocket.Server Websocket websocket.Server
@ -217,9 +199,17 @@ func New(cfg ...config.Iris) *Framework {
{ {
///NOTE: set all with s.Config pointer ///NOTE: set all with s.Config pointer
// set the Logger // set the Logger
s.Logger = logger.New(s.Config.Logger) s.Logger = logger.New(logger.DefaultConfig())
// set the plugin container // set the plugin container
s.Plugins = &pluginContainer{logger: s.Logger} s.Plugins = &pluginContainer{logger: s.Logger}
// set the templates
s.templates = &TemplateEngines{
helpers: map[string]interface{}{
"url": s.URL,
"urlpath": s.Path,
},
engines: make([]*TemplateEngineWrapper, 0),
}
// set the websocket server // set the websocket server
s.Websocket = websocket.NewServer(s.Config.Websocket) s.Websocket = websocket.NewServer(s.Config.Websocket)
// set the servemux, which will provide us the public API also, with its context pool // set the servemux, which will provide us the public API also, with its context pool
@ -241,10 +231,18 @@ func (s *Framework) initialize() {
} }
// set the rest // set the rest
s.rest = rest.New(s.Config.Render.Rest) s.rest = rest.New(s.Config.Rest)
// prepare the templates if enabled
// set templates if not already setted if !s.Config.DisableTemplateEngines {
s.prepareTemplates() if err := s.templates.loadAll(); err != nil {
s.Logger.Panic(err) // panic on templates loading before listening if we have an error.
}
// check and prepare the templates
if len(s.templates.engines) == 0 { // no template engine is registered, let's use the default
s.UseEngine(html.New())
}
s.templates.setReload(s.Config.IsDevelopment)
}
// listen to websocket connections // listen to websocket connections
websocket.RegisterServer(s, s.Websocket, s.Logger) websocket.RegisterServer(s, s.Websocket, s.Logger)
@ -258,22 +256,6 @@ func (s *Framework) initialize() {
} }
} }
// prepareTemplates sets the templates if not nil, we make this check because of .TemplateString, which can be called before Listen
func (s *Framework) prepareTemplates() {
// prepare the templates
if s.templates == nil {
// These functions are directly contact with Iris' functionality.
funcs := map[string]interface{}{
"url": s.URL,
"urlpath": s.Path,
}
template.RegisterSharedFuncs(funcs)
s.templates = template.New(s.Config.Render.Template)
}
}
// Go starts the iris station, listens to all registered servers, and prepare only if Virtual // Go starts the iris station, listens to all registered servers, and prepare only if Virtual
func Go() error { func Go() error {
return Default.Go() return Default.Go()
@ -290,7 +272,6 @@ func (s *Framework) Go() error {
// print the banner // print the banner
if !s.Config.DisableBanner { if !s.Config.DisableBanner {
openedServers := s.Servers.GetAllOpened() openedServers := s.Servers.GetAllOpened()
l := len(openedServers) l := len(openedServers)
hosts := make([]string, l, l) hosts := make([]string, l, l)
@ -495,6 +476,31 @@ func (s *Framework) Close() error {
return s.Servers.CloseAll() return s.Servers.CloseAll()
} }
/*
// set the template engines
s.renderer = &renderer{
engines: make([]TemplateEngine, 0),
buffer: utils.NewBufferPool(64),
helpers: map[string]interface{}{
"url": s.URL,
"urlpath": s.Path,
},
contentType: s.Config.Render.Template.ContentType + "; " + s.Config.Render.Template.Charset,
}*/
// UseEngine adds a template engine to the iris view system
// it does not build/load them yet
func UseEngine(e TemplateEngine) *TemplateEngineLocation {
return Default.UseEngine(e)
}
// UseEngine adds a template engine to the iris view system
// it does not build/load them yet
func (s *Framework) UseEngine(e TemplateEngine) *TemplateEngineLocation {
return s.templates.Add(e)
}
// UseGlobal registers Handler middleware to the beginning, prepends them instead of append // UseGlobal registers Handler middleware to the beginning, prepends them instead of append
// //
// Use it when you want to add a global middleware to all parties, to all routes in all subdomains // Use it when you want to add a global middleware to all parties, to all routes in all subdomains
@ -713,17 +719,19 @@ func (s *Framework) URL(routeName string, args ...interface{}) (url string) {
return return
} }
// TemplateString executes a template and returns its result as string, useful when you want it for sending rich e-mails // TemplateString executes a template from the default template engine and returns its result as string, useful when you want it for sending rich e-mails
// returns empty string on error // returns empty string on error
func TemplateString(templateFile string, pageContext interface{}, layout ...string) string { func TemplateString(templateFile string, pageContext interface{}, options ...map[string]interface{}) string {
return Default.TemplateString(templateFile, pageContext, layout...) return Default.TemplateString(templateFile, pageContext, options...)
} }
// TemplateString executes a template and returns its result as string, useful when you want it for sending rich e-mails // TemplateString executes a template from the default template engine and returns its result as string, useful when you want it for sending rich e-mails
// returns empty string on error // returns empty string on error
func (s *Framework) TemplateString(templateFile string, pageContext interface{}, layout ...string) string { func (s *Framework) TemplateString(templateFile string, pageContext interface{}, options ...map[string]interface{}) string {
s.prepareTemplates() if s.Config.DisableTemplateEngines {
res, err := s.templates.RenderString(templateFile, pageContext, layout...) return ""
}
res, err := s.templates.GetBy(templateFile).ExecuteToString(templateFile, pageContext, options...)
if err != nil { if err != nil {
return "" return ""
} }

View File

@ -3,10 +3,9 @@ package main
import ( import (
"os" "os"
"github.com/iris-contrib/logger"
"github.com/kataras/cli" "github.com/kataras/cli"
"github.com/kataras/iris" "github.com/kataras/iris"
"github.com/kataras/iris/config"
"github.com/kataras/iris/logger"
) )
const ( const (
@ -49,7 +48,7 @@ func init() {
app.Command(runAndWatchCmd) app.Command(runAndWatchCmd)
// init the logger // init the logger
printer = logger.New(config.DefaultLogger()) printer = logger.New(logger.DefaultConfig())
} }
func main() { func main() {

View File

@ -1,6 +0,0 @@
## Package information
I decide to split the logger from the main iris package because logger.go doesn't depends on any of the iris' types.
**Examples and more info will be added soon.**

View File

@ -1,186 +0,0 @@
package logger
import (
"os"
"github.com/fatih/color"
"github.com/kataras/iris/config"
"github.com/mattn/go-colorable"
)
var (
// Prefix is the prefix for the logger, default is [IRIS]
Prefix = "[IRIS] "
// bannersRan keeps track of the logger's print banner count
bannersRan = 0
)
// Logger the logger
type Logger struct {
config *config.Logger
underline *color.Color
}
// attr takes a color integer and converts it to color.Attribute
func attr(sgr int) color.Attribute {
return color.Attribute(sgr)
}
// check if background color > 0 and if so then set it
func (l *Logger) setBg(sgr int) {
if sgr > 0 {
l.underline.Add(attr(sgr))
}
}
// New creates a new Logger from config.Logger configuration
func New(c config.Logger) *Logger {
color.Output = colorable.NewColorable(c.Out)
l := &Logger{&c, color.New(attr(c.ColorFgDefault))}
l.setBg(c.ColorBgDefault)
return l
}
// SetEnable true enables, false disables the Logger
func (l *Logger) SetEnable(enable bool) {
l.config.Disabled = !enable
}
// IsEnabled returns true if Logger is enabled, otherwise false
func (l *Logger) IsEnabled() bool {
return !l.config.Disabled
}
// ResetColors sets the colors to the default
// this func is called every time a success, info, warning, or danger message is printed
func (l *Logger) ResetColors() {
l.underline.Add(attr(l.config.ColorFgDefault))
l.setBg(l.config.ColorBgDefault)
}
// PrintBanner prints a text (banner) with BannerFgColor, BannerBgColor and a success message at the end
// It doesn't cares if the logger is disabled or not, it will print this
func (l *Logger) PrintBanner(banner string, successMessage string) {
c := color.New(attr(l.config.ColorFgBanner))
if l.config.ColorBgDefault > 0 {
c.Add(attr(l.config.ColorBgDefault))
}
c.Println(banner)
bannersRan++
if successMessage != "" {
c.Add(attr(l.config.ColorFgSuccess))
if l.config.ColorBgSuccess > 0 {
c.Add(attr(l.config.ColorBgSuccess))
}
if bannersRan > 1 {
c.Printf("Server[%#v]\n", bannersRan)
}
c.Println(successMessage)
}
c.DisableColor()
c = nil
}
// Printf calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Printf(format string, a ...interface{}) {
if !l.config.Disabled {
l.underline.Printf(l.config.Prefix+format, a...)
}
}
// Print calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Print(a interface{}) {
if !l.config.Disabled {
l.ResetColors()
l.Printf("%#v", a)
}
}
// Println calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Println.
func (l *Logger) Println(a interface{}) {
if !l.config.Disabled {
l.Printf("%#v\n", a)
}
}
// Fatal is equivalent to l.Dangerf("%#v",interface{}) followed by a call to panic().
func (l *Logger) Fatal(a interface{}) {
l.Warningf("%#v", a)
panic("")
}
// Fatalf is equivalent to l.Warningf() followed by a call to os.Exit(1).
func (l *Logger) Fatalf(format string, a ...interface{}) {
l.Warningf(format, a...)
os.Exit(1)
}
// Panic is equivalent to l.Dangerf("%#v",interface{}) followed by a call to panic().
func (l *Logger) Panic(a interface{}) {
l.Dangerf("%s\n", a)
panic(a)
}
// Panicf is equivalent to l.Dangerf() followed by a call to panic().
func (l *Logger) Panicf(format string, a ...interface{}) {
l.Dangerf(format, a...)
panic("")
}
// Successf calls l.Output to print to the logger with the Success colors.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Successf(format string, a ...interface{}) {
if !l.config.Disabled {
l.underline.Add(attr(l.config.ColorFgSuccess))
l.setBg(l.config.ColorBgSuccess)
l.Printf(format, a...)
}
}
// Infof calls l.Output to print to the logger with the Info colors.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Infof(format string, a ...interface{}) {
if !l.config.Disabled {
l.underline.Add(attr(l.config.ColorFgInfo))
l.setBg(l.config.ColorBgInfo)
l.Printf(format, a...)
}
}
// Warningf calls l.Output to print to the logger with the Warning colors.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Warningf(format string, a ...interface{}) {
if !l.config.Disabled {
l.underline.Add(attr(l.config.ColorFgWarning))
l.setBg(l.config.ColorBgWarning)
l.Printf(format, a...)
}
}
// Dangerf calls l.Output to print to the logger with the Danger colors.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Dangerf(format string, a ...interface{}) {
if !l.config.Disabled {
l.underline.Add(attr(l.config.ColorFgDanger))
l.setBg(l.config.ColorBgDanger)
l.Printf(format, a...)
}
}
// Otherf calls l.Output to print to the logger with the Other colors.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Otherf(format string, a ...interface{}) {
if !l.config.Disabled {
l.underline.Add(attr(l.config.ColorFgOther))
l.setBg(l.config.ColorBgOther)
l.Printf(format, a...)
}
}

View File

@ -5,7 +5,7 @@ import (
"github.com/iris-contrib/errors" "github.com/iris-contrib/errors"
"github.com/kataras/iris/logger" "github.com/iris-contrib/logger"
"github.com/kataras/iris/utils" "github.com/kataras/iris/utils"
) )

View File

@ -1,317 +0,0 @@
package rest
import (
"bytes"
"encoding/json"
"encoding/xml"
"github.com/klauspost/compress/gzip"
"github.com/valyala/fasthttp"
)
// Engine is the generic interface for all responses.
type Engine interface {
Render(*fasthttp.RequestCtx, interface{}) error
//used only if config gzip is enabled
RenderGzip(*fasthttp.RequestCtx, interface{}) error
}
// Head defines the basic ContentType and Status fields.
type Head struct {
ContentType string
Status int
}
// Data built-in renderer.
type Data struct {
Head
}
// JSON built-in renderer.
type JSON struct {
Head
Indent bool
UnEscapeHTML bool
Prefix []byte
StreamingJSON bool
}
// JSONP built-in renderer.
type JSONP struct {
Head
Indent bool
Callback string
}
// Text built-in renderer.
type Text struct {
Head
}
// XML built-in renderer.
type XML struct {
Head
Indent bool
Prefix []byte
}
// Write outputs the header content.
func (h Head) Write(ctx *fasthttp.RequestCtx) {
ctx.Response.Header.Set(ContentType, h.ContentType)
ctx.SetStatusCode(h.Status)
}
// Render a data response.
func (d Data) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
c := string(ctx.Request.Header.Peek(ContentType))
w := ctx.Response.BodyWriter()
if c != "" {
d.Head.ContentType = c
}
d.Head.Write(ctx)
w.Write(v.([]byte))
return nil
}
// RenderGzip a data response using gzip compression.
func (d Data) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
c := string(ctx.Request.Header.Peek(ContentType))
if c != "" {
d.Head.ContentType = c
}
d.Head.Write(ctx)
_, err := fasthttp.WriteGzip(ctx.Response.BodyWriter(), v.([]byte))
if err == nil {
ctx.Response.Header.Add("Content-Encoding", "gzip")
}
return err
}
// Render a JSON response.
func (j JSON) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
if j.StreamingJSON {
return j.renderStreamingJSON(ctx, v)
}
var result []byte
var err error
if j.Indent {
result, err = json.MarshalIndent(v, "", " ")
result = append(result, '\n')
} else {
result, err = json.Marshal(v)
}
if err != nil {
return err
}
// Unescape HTML if needed.
if j.UnEscapeHTML {
result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1)
result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1)
result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1)
}
w := ctx.Response.BodyWriter()
// JSON marshaled fine, write out the result.
j.Head.Write(ctx)
if len(j.Prefix) > 0 {
w.Write(j.Prefix)
}
w.Write(result)
return nil
}
// RenderGzip a JSON response using gzip compression.
func (j JSON) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
if j.StreamingJSON {
return j.renderStreamingJSONGzip(ctx, v)
}
var result []byte
var err error
if j.Indent {
result, err = json.MarshalIndent(v, "", " ")
result = append(result, '\n')
} else {
result, err = json.Marshal(v)
}
if err != nil {
return err
}
ctx.Response.Header.Add("Content-Encoding", "gzip")
// Unescape HTML if needed.
if j.UnEscapeHTML {
result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1)
result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1)
result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1)
}
w := gzip.NewWriter(ctx.Response.BodyWriter())
// JSON marshaled fine, write out the result.
j.Head.Write(ctx)
if len(j.Prefix) > 0 {
w.Write(j.Prefix)
}
w.Write(result)
w.Close()
return nil
}
func (j JSON) renderStreamingJSON(ctx *fasthttp.RequestCtx, v interface{}) error {
j.Head.Write(ctx)
w := ctx.Response.BodyWriter()
if len(j.Prefix) > 0 {
w.Write(j.Prefix)
}
return json.NewEncoder(w).Encode(v)
}
func (j JSON) renderStreamingJSONGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
ctx.Response.Header.Add("Content-Encoding", "gzip")
j.Head.Write(ctx)
w := gzip.NewWriter(ctx.Response.BodyWriter())
if len(j.Prefix) > 0 {
w.Write(j.Prefix)
}
w.Close()
return json.NewEncoder(w).Encode(v)
}
// Render a JSONP response.
func (j JSONP) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
var result []byte
var err error
if j.Indent {
result, err = json.MarshalIndent(v, "", " ")
} else {
result, err = json.Marshal(v)
}
if err != nil {
return err
}
w := ctx.Response.BodyWriter()
// JSON marshaled fine, write out the result.
j.Head.Write(ctx)
w.Write([]byte(j.Callback + "("))
w.Write(result)
w.Write([]byte(");"))
// If indenting, append a new line.
if j.Indent {
w.Write([]byte("\n"))
}
return nil
}
// RenderGzip a JSONP response using gzip compression.
func (j JSONP) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
var result []byte
var err error
if j.Indent {
result, err = json.MarshalIndent(v, "", " ")
} else {
result, err = json.Marshal(v)
}
if err != nil {
return err
}
w := gzip.NewWriter(ctx.Response.BodyWriter())
ctx.Response.Header.Add("Content-Encoding", "gzip")
// JSON marshaled fine, write out the result.
j.Head.Write(ctx)
w.Write([]byte(j.Callback + "("))
w.Write(result)
w.Write([]byte(");"))
// If indenting, append a new line.
if j.Indent {
w.Write([]byte("\n"))
}
w.Close()
return nil
}
// Render a text response.
func (t Text) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
c := string(ctx.Request.Header.Peek(ContentType))
if c != "" {
t.Head.ContentType = c
}
w := ctx.Response.BodyWriter()
t.Head.Write(ctx)
w.Write([]byte(v.(string)))
return nil
}
// RenderGzip a Text response using gzip compression.
func (t Text) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
c := string(ctx.Request.Header.Peek(ContentType))
if c != "" {
t.Head.ContentType = c
}
ctx.Response.Header.Add("Content-Encoding", "gzip")
t.Head.Write(ctx)
fasthttp.WriteGzip(ctx.Response.BodyWriter(), []byte(v.(string)))
return nil
}
// Render an XML response.
func (x XML) Render(ctx *fasthttp.RequestCtx, v interface{}) error {
var result []byte
var err error
if x.Indent {
result, err = xml.MarshalIndent(v, "", " ")
result = append(result, '\n')
} else {
result, err = xml.Marshal(v)
}
if err != nil {
return err
}
// XML marshaled fine, write out the result.
x.Head.Write(ctx)
w := ctx.Response.BodyWriter()
if len(x.Prefix) > 0 {
w.Write(x.Prefix)
}
w.Write(result)
return nil
}
// RenderGzip an XML response using gzip compression.
func (x XML) RenderGzip(ctx *fasthttp.RequestCtx, v interface{}) error {
var result []byte
var err error
if x.Indent {
result, err = xml.MarshalIndent(v, "", " ")
result = append(result, '\n')
} else {
result, err = xml.Marshal(v)
}
if err != nil {
return err
}
ctx.Response.Header.Add("Content-Encoding", "gzip")
// XML marshaled fine, write out the result.
x.Head.Write(ctx)
w := gzip.NewWriter(ctx.Response.BodyWriter())
if len(x.Prefix) > 0 {
w.Write(x.Prefix)
}
w.Write(result)
w.Close()
return nil
}

View File

@ -1,173 +0,0 @@
// Package rest is an edited file of https://github.com/unrolled/render
package rest
import (
"github.com/kataras/iris/config"
"github.com/kataras/iris/utils"
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday"
"github.com/valyala/fasthttp"
)
const (
// ContentBinary header value for binary data.
ContentBinary = "application/octet-stream"
// ContentJSON header value for JSON data.
ContentJSON = "application/json"
// ContentJSONP header value for JSONP data.
ContentJSONP = "application/javascript"
// ContentLength header constant.
ContentLength = "Content-Length"
// ContentText header value for Text data.
ContentText = "text/plain"
// ContentType header constant.
ContentType = "Content-Type"
// ContentXML header value for XML data.
ContentXML = "text/xml"
)
// bufPool represents a reusable buffer pool for executing templates into.
var bufPool *utils.BufferPool
// Render is a service that provides functions for easily writing JSON, XML,
// binary data, and HTML templates out to a HTTP Response.
type Render struct {
// Customize Secure with an Options struct.
Config config.Rest
CompiledCharset string
}
// New constructs a new Render instance with the supplied configs.
func New(cfg ...config.Rest) *Render {
if bufPool == nil {
bufPool = utils.NewBufferPool(64)
}
c := config.DefaultRest().Merge(cfg)
r := &Render{
Config: c,
}
r.prepareConfig()
return r
}
func (r *Render) prepareConfig() {
// Fill in the defaults if need be.
if len(r.Config.Charset) == 0 {
r.Config.Charset = config.Charset
}
r.CompiledCharset = "; charset=" + r.Config.Charset
}
// Render is the generic function called by XML, JSON, Data, HTML, and can be called by custom implementations.
func (r *Render) Render(ctx *fasthttp.RequestCtx, e Engine, data interface{}) error {
var err error
if r.Config.Gzip {
err = e.RenderGzip(ctx, data)
} else {
err = e.Render(ctx, data)
}
if err != nil && !r.Config.DisableHTTPErrorRendering {
ctx.Response.SetBodyString(err.Error())
ctx.Response.SetStatusCode(fasthttp.StatusInternalServerError)
}
return err
}
// Data writes out the raw bytes as binary data.
func (r *Render) Data(ctx *fasthttp.RequestCtx, status int, v []byte) error {
head := Head{
ContentType: ContentBinary,
Status: status,
}
d := Data{
Head: head,
}
return r.Render(ctx, d, v)
}
// JSON marshals the given interface object and writes the JSON response.
func (r *Render) JSON(ctx *fasthttp.RequestCtx, status int, v interface{}) error {
head := Head{
ContentType: ContentJSON + r.CompiledCharset,
Status: status,
}
j := JSON{
Head: head,
Indent: r.Config.IndentJSON,
Prefix: r.Config.PrefixJSON,
UnEscapeHTML: r.Config.UnEscapeHTML,
StreamingJSON: r.Config.StreamingJSON,
}
return r.Render(ctx, j, v)
}
// JSONP marshals the given interface object and writes the JSON response.
func (r *Render) JSONP(ctx *fasthttp.RequestCtx, status int, callback string, v interface{}) error {
head := Head{
ContentType: ContentJSONP + r.CompiledCharset,
Status: status,
}
j := JSONP{
Head: head,
Indent: r.Config.IndentJSON,
Callback: callback,
}
return r.Render(ctx, j, v)
}
// Text writes out a string as plain text.
func (r *Render) Text(ctx *fasthttp.RequestCtx, status int, v string) error {
head := Head{
ContentType: ContentText + r.CompiledCharset,
Status: status,
}
t := Text{
Head: head,
}
return r.Render(ctx, t, v)
}
// XML marshals the given interface object and writes the XML response.
func (r *Render) XML(ctx *fasthttp.RequestCtx, status int, v interface{}) error {
head := Head{
ContentType: ContentXML + r.CompiledCharset,
Status: status,
}
x := XML{
Head: head,
Indent: r.Config.IndentXML,
Prefix: r.Config.PrefixXML,
}
return r.Render(ctx, x, v)
}
// Markdown parses and returns the converted html from a markdown []byte
// accepts two parameters
// first is the http status code
// second is the markdown string
//
// Note that: Works different than the other rest's functions.
func (r *Render) Markdown(markdownBytes []byte) string {
buf := blackfriday.MarkdownCommon(markdownBytes)
if r.Config.MarkdownSanitize {
buf = bluemonday.UGCPolicy().SanitizeBytes(buf)
}
return string(buf)
}

View File

@ -1,8 +0,0 @@
# Folder Information
This folder contains the template support for Iris. The folder name is singular (template) so the `/template/engine`, because you can use **ONLY ONE** at the same time.
## How to use
**Refer to the Book**

View File

@ -1,80 +0,0 @@
package amber
import (
"html/template"
"fmt"
"io"
"path/filepath"
"sync"
"github.com/eknkc/amber"
"github.com/kataras/iris/config"
)
// Engine the amber template engine
type Engine struct {
Config *config.Template
templateCache map[string]*template.Template
mu sync.Mutex
}
// New creates and returns a new amber engine
func New(cfg config.Template) *Engine {
return &Engine{Config: &cfg}
}
// BuildTemplates builds the amber templates
func (e *Engine) BuildTemplates() error {
opt := amber.DirOptions{}
opt.Recursive = true
if e.Config.Extensions == nil || len(e.Config.Extensions) == 0 {
e.Config.Extensions = []string{".html"}
}
// prepare the global amber funcs
funcs := template.FuncMap{}
for k, v := range amber.FuncMap { // add the amber's default funcs
funcs[k] = v
}
if e.Config.Amber.Funcs != nil { // add the config's funcs
for k, v := range e.Config.Amber.Funcs {
funcs[k] = v
}
}
amber.FuncMap = funcs //set the funcs
opt.Ext = e.Config.Extensions[0]
templates, err := amber.CompileDir(e.Config.Directory, opt, amber.DefaultOptions) // this returns the map with stripped extension, we want extension so we copy the map
if err == nil {
e.templateCache = make(map[string]*template.Template)
for k, v := range templates {
name := filepath.ToSlash(k + opt.Ext)
e.templateCache[name] = v
delete(templates, k)
}
}
return err
}
func (e *Engine) fromCache(relativeName string) *template.Template {
e.mu.Lock()
tmpl, ok := e.templateCache[relativeName]
if ok {
e.mu.Unlock()
return tmpl
}
e.mu.Unlock()
return nil
}
// ExecuteWriter executes a templates and write its results to the out writer
func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
if tmpl := e.fromCache(name); tmpl != nil {
return tmpl.ExecuteTemplate(out, name, binding)
}
return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, e.Config.Directory)
}

View File

@ -1,162 +0,0 @@
// Package handlebars the HandlebarsEngine's functionality
package handlebars
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"github.com/aymerick/raymond"
"github.com/kataras/iris/config"
)
type (
// Engine the Handlebars engine
Engine struct {
Config *config.Template
templateCache map[string]*raymond.Template
mu sync.Mutex
}
)
// New creates and returns the Handlebars template engine
func New(c config.Template) *Engine {
s := &Engine{Config: &c, templateCache: make(map[string]*raymond.Template, 0)}
return s
}
// BuildTemplates builds the handlebars templates
func (e *Engine) BuildTemplates() error {
if e.Config.Extensions == nil || len(e.Config.Extensions) == 0 {
e.Config.Extensions = []string{".html"}
}
// register the global helpers
if e.Config.Handlebars.Helpers != nil {
raymond.RegisterHelpers(e.Config.Handlebars.Helpers)
}
// 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)
raymond.RegisterHelper("render", func(partial string, binding interface{}) raymond.SafeString {
contents, err := e.executeTemplateBuf(partial, binding)
if err != nil {
return raymond.SafeString("Template with name: " + partial + " couldn't not be found.")
}
return raymond.SafeString(contents)
})
var templateErr error
dir := e.Config.Directory
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if info == nil || info.IsDir() {
return nil
}
rel, err := filepath.Rel(dir, path)
if err != nil {
return err
}
ext := ""
if strings.Index(rel, ".") != -1 {
ext = filepath.Ext(rel)
}
for _, extension := range e.Config.Extensions {
if ext == extension {
buf, err := ioutil.ReadFile(path)
contents := string(buf)
if err != nil {
templateErr = err
break
}
name := filepath.ToSlash(rel)
tmpl, err := raymond.Parse(contents)
if err != nil {
templateErr = err
continue
}
e.mu.Lock()
e.templateCache[name] = tmpl
e.mu.Unlock()
break
}
}
return nil
})
return templateErr
}
func (e *Engine) fromCache(relativeName string) *raymond.Template {
e.mu.Lock()
tmpl, ok := e.templateCache[relativeName]
if ok {
e.mu.Unlock()
return tmpl
}
e.mu.Unlock()
return nil
}
func (e *Engine) executeTemplateBuf(name string, binding interface{}) (string, error) {
if tmpl := e.fromCache(name); tmpl != nil {
return tmpl.Exec(binding)
}
return "", nil
}
// ExecuteWriter executes a templates and write its results to the out writer
func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
isLayout := false
renderFilename := name
if layout != "" {
isLayout = true
renderFilename = layout // the render becomes the layout, and the name is the partial.
}
if tmpl := e.fromCache(renderFilename); tmpl != nil {
if isLayout {
var context map[string]interface{}
if m, is := binding.(map[string]interface{}); is { //handlebars accepts maps,
context = m
} else {
return fmt.Errorf("Please provide a map[string]interface{} type as the binding instead of the %#v", binding)
}
contents, err := e.executeTemplateBuf(name, binding)
if err != nil {
return err
}
if context == nil {
context = make(map[string]interface{}, 1)
}
// I'm implemented the {{ yield }} as with the rest of template engines, so this is not inneed for iris, but the user can do that manually if want
// there is no performanrce different: raymond.RegisterPartialTemplate(name, tmpl)
context["yield"] = raymond.SafeString(contents)
}
res, err := tmpl.Exec(binding)
if err != nil {
return err
}
_, err = fmt.Fprint(out, res)
return err
}
return fmt.Errorf("[IRIS TEMPLATES] Template with name %s[original name = %s] doesn't exists in the dir %s", renderFilename, name, e.Config.Directory)
}

View File

@ -1,245 +0,0 @@
package html
import (
"bytes"
"fmt"
"html/template"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/Joker/jade"
"github.com/kataras/iris/config"
)
type (
// Engine the html/template engine & Jade
Engine struct {
Config *config.Template
Templates *template.Template
}
)
var emptyFuncs = template.FuncMap{
"yield": func() (string, error) {
return "", fmt.Errorf("yield was called, yet no layout defined")
},
"partial": func() (string, error) {
return "", fmt.Errorf("block was called, yet no layout defined")
},
"current": func() (string, error) {
return "", nil
}, "render": func() (string, error) {
return "", nil
},
}
// New creates and returns the HTMLTemplate template engine
func New(c config.Template) *Engine {
s := &Engine{Config: &c}
return s
}
// BuildTemplates builds the templates
func (s *Engine) BuildTemplates() error {
if s.Config.Asset == nil || s.Config.AssetNames == nil {
return s.buildFromDir()
}
return s.buildFromAsset()
}
func (s *Engine) buildFromDir() error {
if s.Config.Directory == "" {
return nil //we don't return fill error here(yet)
}
var templateErr error
/*var minifier *minify.M
if s.Config.Minify {
minifier = minify.New()
minifier.AddFunc("text/html", htmlMinifier.Minify)
} // Note: minifier has bugs, I complety remove this from Iris.
*/
dir := s.Config.Directory
s.Templates = template.New(dir)
s.Templates.Delims(s.Config.HTMLTemplate.Left, s.Config.HTMLTemplate.Right)
// Walk the supplied directory and compile any files that match our extension list.
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if info == nil || info.IsDir() {
return nil
}
rel, err := filepath.Rel(dir, path)
if err != nil {
return err
}
ext := ""
if strings.Index(rel, ".") != -1 {
ext = filepath.Ext(rel)
}
for _, extension := range s.Config.Extensions {
if ext == extension {
buf, err := ioutil.ReadFile(path)
contents := string(buf)
/*if s.Config.Minify {
buf, err = minifier.Bytes("text/html", buf)
}*/
if err != nil {
templateErr = err
break
}
name := filepath.ToSlash(rel)
tmpl := s.Templates.New(name)
if s.Config.Engine == config.JadeEngine {
contents, err = jade.Parse(name, contents)
}
if err != nil {
templateErr = err
break
}
// Add our funcmaps.
if s.Config.HTMLTemplate.Funcs != nil {
tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
}
tmpl.Funcs(emptyFuncs).Parse(contents)
break
}
}
return nil
})
return templateErr
}
func (s *Engine) buildFromAsset() error {
var templateErr error
dir := s.Config.Directory
s.Templates = template.New(dir)
s.Templates.Delims(s.Config.HTMLTemplate.Left, s.Config.HTMLTemplate.Right)
for _, path := range s.Config.AssetNames() {
if !strings.HasPrefix(path, dir) {
continue
}
rel, err := filepath.Rel(dir, path)
if err != nil {
panic(err)
}
ext := ""
if strings.Index(rel, ".") != -1 {
ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")
}
for _, extension := range s.Config.Extensions {
if ext == extension {
buf, err := s.Config.Asset(path)
if err != nil {
panic(err)
}
contents := string(buf)
name := filepath.ToSlash(rel)
tmpl := s.Templates.New(name)
if s.Config.Engine == config.JadeEngine {
contents, err = jade.Parse(name, contents)
}
if err != nil {
return err
}
// Add our funcmaps.
if s.Config.HTMLTemplate.Funcs != nil {
tmpl.Funcs(s.Config.HTMLTemplate.Funcs)
}
tmpl.Funcs(emptyFuncs).Parse(contents)
break
}
}
}
return templateErr
}
func (s *Engine) executeTemplateBuf(name string, binding interface{}) (*bytes.Buffer, error) {
buf := new(bytes.Buffer)
err := s.Templates.ExecuteTemplate(buf, name, binding)
return buf, err
}
func (s *Engine) layoutFuncsFor(name string, binding interface{}) {
funcs := template.FuncMap{
"yield": func() (template.HTML, error) {
buf, err := s.executeTemplateBuf(name, binding)
// Return safe HTML here since we are rendering our own template.
return template.HTML(buf.String()), err
},
"current": func() (string, error) {
return name, nil
},
"partial": func(partialName string) (template.HTML, error) {
fullPartialName := fmt.Sprintf("%s-%s", partialName, name)
if s.Config.HTMLTemplate.RequirePartials || s.Templates.Lookup(fullPartialName) != nil {
buf, err := s.executeTemplateBuf(fullPartialName, binding)
return template.HTML(buf.String()), err
}
return "", nil
},
"render": func(fullPartialName string) (template.HTML, error) {
buf, err := s.executeTemplateBuf(fullPartialName, binding)
return template.HTML(buf.String()), err
},
}
_userLayoutFuncs := s.Config.HTMLTemplate.LayoutFuncs
if _userLayoutFuncs != nil && len(_userLayoutFuncs) > 0 {
for k, v := range _userLayoutFuncs {
funcs[k] = v
}
}
if tpl := s.Templates.Lookup(name); tpl != nil {
tpl.Funcs(funcs)
}
}
func (s *Engine) runtimeFuncsFor(name string, binding interface{}) {
funcs := template.FuncMap{
"render": func(fullPartialName string) (template.HTML, error) {
buf, err := s.executeTemplateBuf(fullPartialName, binding)
return template.HTML(buf.String()), err
},
}
if tpl := s.Templates.Lookup(name); tpl != nil {
tpl.Funcs(funcs)
}
}
// ExecuteWriter executes a templates and write its results to the out writer
func (s *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
if layout != "" {
s.layoutFuncsFor(name, binding)
name = layout
} else {
s.runtimeFuncsFor(name, binding)
}
return s.Templates.ExecuteTemplate(out, name, binding)
}

View File

@ -1,15 +0,0 @@
// Package jade the JadeEngine's functionality lives inside ../html now
package jade
import (
"github.com/kataras/iris/config"
"github.com/kataras/iris/render/template/engine/html"
)
// New creates and returns the HTMLTemplate template engine
func New(c config.Template) *html.Engine {
// copy the Jade to the HTMLTemplate
c.HTMLTemplate = config.HTMLTemplate(c.Jade)
s := &html.Engine{Config: &c}
return s
}

View File

@ -1,155 +0,0 @@
package markdown
import (
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"fmt"
"github.com/kataras/iris/config"
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday"
)
// Supports RAW markdown only, no context binding or layout, to use dynamic markdown with other template engine use the context.Markdown/MarkdownString
type (
// Engine the jade engine
Engine struct {
Config *config.Template
templateCache map[string][]byte
mu sync.Mutex
}
)
// New creates and returns a Pongo template engine
func New(c config.Template) *Engine {
return &Engine{Config: &c, templateCache: make(map[string][]byte)}
}
// BuildTemplates builds the templates
func (e *Engine) BuildTemplates() error {
if e.Config.Asset == nil || e.Config.AssetNames == nil {
return e.buildFromDir()
}
return e.buildFromAsset()
}
func (e *Engine) buildFromDir() (templateErr error) {
if e.Config.Directory == "" {
return nil //we don't return fill error here(yet)
}
dir := e.Config.Directory
// Walk the supplied directory and compile any files that match our extension list.
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if info == nil || info.IsDir() {
return nil
}
rel, err := filepath.Rel(dir, path)
if err != nil {
return err
}
ext := ""
if strings.Index(rel, ".") != -1 {
ext = filepath.Ext(rel)
}
for _, extension := range e.Config.Extensions {
if ext == extension {
buf, err := ioutil.ReadFile(path)
if err != nil {
templateErr = err
break
}
buf = blackfriday.MarkdownCommon(buf)
if e.Config.Markdown.Sanitize {
buf = bluemonday.UGCPolicy().SanitizeBytes(buf)
}
if err != nil {
templateErr = err
break
}
name := filepath.ToSlash(rel)
e.templateCache[name] = buf
break
}
}
return nil
})
return nil
}
func (e *Engine) buildFromAsset() error {
var templateErr error
dir := e.Config.Directory
for _, path := range e.Config.AssetNames() {
if !strings.HasPrefix(path, dir) {
continue
}
rel, err := filepath.Rel(dir, path)
if err != nil {
panic(err)
}
ext := ""
if strings.Index(rel, ".") != -1 {
ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")
}
for _, extension := range e.Config.Extensions {
if ext == extension {
buf, err := e.Config.Asset(path)
if err != nil {
templateErr = err
break
}
b := blackfriday.MarkdownCommon(buf)
if e.Config.Markdown.Sanitize {
b = bluemonday.UGCPolicy().SanitizeBytes(b)
}
name := filepath.ToSlash(rel)
e.templateCache[name] = b
break
}
}
}
return templateErr
}
func (e *Engine) fromCache(relativeName string) []byte {
e.mu.Lock()
tmpl, ok := e.templateCache[relativeName]
if ok {
e.mu.Unlock() // defer is slow
return tmpl
}
e.mu.Unlock() // defer is slow
return nil
}
// ExecuteWriter executes a templates and write its results to the out writer
// layout here is useless
func (e *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
if tmpl := e.fromCache(name); tmpl != nil {
_, err := out.Write(tmpl)
return err
}
return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, e.Config.Directory)
}

View File

@ -1,183 +0,0 @@
package pongo
import (
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"fmt"
"github.com/flosch/pongo2"
"github.com/kataras/iris/config"
)
type (
// Engine the pongo2 engine
Engine struct {
Config *config.Template
templateCache map[string]*pongo2.Template
mu sync.Mutex
}
)
// New creates and returns a Pongo template engine
func New(c config.Template) *Engine {
return &Engine{Config: &c, templateCache: make(map[string]*pongo2.Template)}
}
// BuildTemplates builds the templates
func (p *Engine) BuildTemplates() error {
// Add our filters. first
for k, v := range p.Config.Pongo.Filters {
pongo2.RegisterFilter(k, v)
}
if p.Config.Asset == nil || p.Config.AssetNames == nil {
return p.buildFromDir()
}
return p.buildFromAsset()
}
func (p *Engine) buildFromDir() (templateErr error) {
if p.Config.Directory == "" {
return nil //we don't return fill error here(yet)
}
dir := p.Config.Directory
fsLoader, err := pongo2.NewLocalFileSystemLoader(dir) // I see that this doesn't read the content if already parsed, so do it manually via filepath.Walk
if err != nil {
return err
}
set := pongo2.NewSet("", fsLoader)
set.Globals = getPongoContext(p.Config.Pongo.Globals)
// Walk the supplied directory and compile any files that match our extension list.
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
// Fix same-extension-dirs bug: some dir might be named to: "users.tmpl", "local.html".
// These dirs should be excluded as they are not valid golang templates, but files under
// them should be treat as normal.
// If is a dir, return immediately (dir is not a valid golang template).
if info == nil || info.IsDir() {
return nil
}
rel, err := filepath.Rel(dir, path)
if err != nil {
return err
}
ext := ""
if strings.Index(rel, ".") != -1 {
ext = filepath.Ext(rel)
}
for _, extension := range p.Config.Extensions {
if ext == extension {
buf, err := ioutil.ReadFile(path)
if err != nil {
templateErr = err
break
}
if err != nil {
templateErr = err
break
}
name := filepath.ToSlash(rel)
p.templateCache[name], templateErr = set.FromString(string(buf))
if templateErr != nil {
return templateErr
}
break
}
}
return nil
})
return
}
func (p *Engine) buildFromAsset() error {
var templateErr error
dir := p.Config.Directory
fsLoader, err := pongo2.NewLocalFileSystemLoader(dir)
if err != nil {
return err
}
set := pongo2.NewSet("", fsLoader)
set.Globals = getPongoContext(p.Config.Pongo.Globals)
for _, path := range p.Config.AssetNames() {
if !strings.HasPrefix(path, dir) {
continue
}
rel, err := filepath.Rel(dir, path)
if err != nil {
panic(err)
}
ext := ""
if strings.Index(rel, ".") != -1 {
ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")
}
for _, extension := range p.Config.Extensions {
if ext == extension {
buf, err := p.Config.Asset(path)
if err != nil {
templateErr = err
break
}
name := filepath.ToSlash(rel)
p.templateCache[name], err = set.FromString(string(buf))
if err != nil {
templateErr = err
break
}
break
}
}
}
return templateErr
}
// getPongoContext returns the pongo2.Context from map[string]interface{} or from pongo2.Context, used internaly
func getPongoContext(templateData interface{}) pongo2.Context {
if templateData == nil {
return nil
}
if contextData, isPongoContext := templateData.(pongo2.Context); isPongoContext {
return contextData
}
return templateData.(map[string]interface{})
}
func (p *Engine) fromCache(relativeName string) *pongo2.Template {
p.mu.Lock() // defer is slow
tmpl, ok := p.templateCache[relativeName]
if ok {
p.mu.Unlock()
return tmpl
}
p.mu.Unlock()
return nil
}
// ExecuteWriter executes a templates and write its results to the out writer
// layout here is useless
func (p *Engine) ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error {
if tmpl := p.fromCache(name); tmpl != nil {
return tmpl.ExecuteWriter(getPongoContext(binding), out)
}
return fmt.Errorf("[IRIS TEMPLATES] Template with name %s doesn't exists in the dir %s", name, p.Config.Directory)
}

View File

@ -1,231 +0,0 @@
package template
import (
"fmt"
"io"
"github.com/klauspost/compress/gzip"
"sync"
"github.com/kataras/iris/config"
"github.com/kataras/iris/context"
"github.com/kataras/iris/render/template/engine/amber"
"github.com/kataras/iris/render/template/engine/handlebars"
"github.com/kataras/iris/render/template/engine/html"
"github.com/kataras/iris/render/template/engine/jade"
"github.com/kataras/iris/render/template/engine/markdown"
"github.com/kataras/iris/render/template/engine/pongo"
"github.com/kataras/iris/utils"
)
type (
// Engine the interface that all template engines must inheritance
Engine interface {
// BuildTemplates builds the templates for a directory
BuildTemplates() error
// ExecuteWriter finds and execute a template and write its result to the out writer
ExecuteWriter(out io.Writer, name string, binding interface{}, layout string) error
}
// Template the internal configs for the common configs for the template engines
Template struct {
// Engine the type of the Engine
Engine Engine
// Gzip enable gzip compression
// default is false
Gzip bool
// IsDevelopment re-builds the templates on each request
// default is false
IsDevelopment bool
// Directory the system path which the templates live
// default is ./templates
Directory string
// Extensions the allowed file extension
// default is []string{".html"}
Extensions []string
// ContentType is the Content-Type response header
// default is text/html but you can change if if needed
ContentType string
// Layout the template file ( with its extension) which is the mother of all
// use it to have it as a root file, and include others with {{ yield }}, refer the docs
Layout string
buffer *utils.BufferPool // this is used only for RenderString
gzipWriterPool sync.Pool
}
)
// sharedFuncs the funcs should be exists in all supported view template engines
var sharedFuncs map[string]interface{}
// we do this because we don't want to override the user's funcs
func setSharedFuncs(source map[string]interface{}, target map[string]interface{}) {
if source == nil {
return
}
if target == nil {
target = make(map[string]interface{}, len(source))
}
for k, v := range source {
if target[k] == nil {
target[k] = v
}
}
}
// New creates and returns a Template instance which keeps the Template Engine and helps with render
func New(c config.Template) *Template {
defer func() {
sharedFuncs = nil
}()
var e Engine
// [ENGINE-2]
switch c.Engine {
case config.HTMLEngine:
setSharedFuncs(sharedFuncs, c.HTMLTemplate.Funcs)
e = html.New(c) // HTMLTemplate
case config.JadeEngine:
setSharedFuncs(sharedFuncs, c.Jade.Funcs)
e = jade.New(c) // Jade
case config.PongoEngine:
setSharedFuncs(sharedFuncs, c.Pongo.Globals)
e = pongo.New(c) // Pongo2
case config.MarkdownEngine:
e = markdown.New(c) // Markdown
case config.AmberEngine:
setSharedFuncs(sharedFuncs, c.Amber.Funcs)
e = amber.New(c) // Amber
case config.HandlebarsEngine:
setSharedFuncs(sharedFuncs, c.Handlebars.Helpers)
e = handlebars.New(c)
default: // config.NoEngine
return nil
}
if err := e.BuildTemplates(); err != nil { // first build the templates, if error then panic because this is called before server's run
panic(err)
}
compiledContentType := c.ContentType + "; charset=" + c.Charset
t := &Template{
Engine: e,
IsDevelopment: c.IsDevelopment,
Gzip: c.Gzip,
ContentType: compiledContentType,
Layout: c.Layout,
buffer: utils.NewBufferPool(64),
gzipWriterPool: sync.Pool{New: func() interface{} {
return &gzip.Writer{}
}},
}
return t
}
// RegisterSharedFunc registers a functionality that should be inherited from all supported template engines
func RegisterSharedFunc(name string, fn interface{}) {
if sharedFuncs == nil {
sharedFuncs = make(map[string]interface{})
}
sharedFuncs[name] = fn
}
// RegisterSharedFuncs registers functionalities that should be inherited from all supported template engines
func RegisterSharedFuncs(theFuncs map[string]interface{}) {
if sharedFuncs == nil || len(sharedFuncs) == 0 {
sharedFuncs = theFuncs
return
}
for k, v := range theFuncs {
sharedFuncs[k] = v
}
}
func (t *Template) getLayout(ctx context.IContext, passedLayouts []string) string {
_layout := ""
if len(passedLayouts) > 0 {
_layout = passedLayouts[0]
} else if ctx != nil {
if layoutFromCtx := ctx.GetString(config.TemplateLayoutContextKey); layoutFromCtx != "" {
_layout = layoutFromCtx
}
}
if _layout == "" && _layout != config.NoLayout {
if t.Layout != config.NoLayout { // the config's Layout is disabled if "" , config.NoLayout should be passed only on ctx.Render but for any case check if user set it as config.NoLayout also
_layout = t.Layout
}
}
return _layout
}
// Render renders a template using the context's writer
func (t *Template) Render(ctx context.IContext, name string, binding interface{}, layout ...string) (err error) {
if t == nil { // No engine was given but .Render was called
ctx.HTML(403, "<b> Iris </b> <br/> Templates are disabled via config.NoEngine, check your iris' configuration please.")
return fmt.Errorf("[IRIS TEMPLATES] Templates are disabled via config.NoEngine, check your iris' configuration please.\n")
}
// build templates again on each render if IsDevelopment.
if t.IsDevelopment {
if err = t.Engine.BuildTemplates(); err != nil {
return
}
}
_layout := t.getLayout(ctx, layout)
ctx.GetRequestCtx().Response.Header.Set("Content-Type", t.ContentType)
var out io.Writer
if t.Gzip {
ctx.GetRequestCtx().Response.Header.Add("Content-Encoding", "gzip")
gzipWriter := t.gzipWriterPool.Get().(*gzip.Writer)
gzipWriter.Reset(ctx.GetRequestCtx().Response.BodyWriter())
defer gzipWriter.Close()
defer t.gzipWriterPool.Put(gzipWriter)
out = gzipWriter
} else {
out = ctx.GetRequestCtx().Response.BodyWriter()
}
err = t.Engine.ExecuteWriter(out, name, binding, _layout)
return
}
// RenderString executes a template and returns its contents result (string)
func (t *Template) RenderString(name string, binding interface{}, layout ...string) (result string, err error) {
if t == nil { // No engine was given but .Render was called
err = fmt.Errorf("[IRIS TEMPLATES] Templates are disabled via config.NoEngine, check your iris' configuration please.\n")
return
}
// build templates again on each render if IsDevelopment.
if t.IsDevelopment {
if err = t.Engine.BuildTemplates(); err != nil {
return
}
}
_layout := t.getLayout(nil, layout)
out := t.buffer.Get()
// if we have problems later consider that -> out.Reset()
defer t.buffer.Put(out)
err = t.Engine.ExecuteWriter(out, name, binding, _layout)
if err == nil {
result = out.String()
}
return
}

277
template.go Normal file
View File

@ -0,0 +1,277 @@
package iris
import (
"compress/gzip"
"io"
"path/filepath"
"sync"
"github.com/iris-contrib/errors"
"github.com/kataras/iris/config"
"github.com/kataras/iris/utils"
)
var (
builtinFuncs = [...]string{"url", "urlpath"}
// these are used inside load all in order when no directory and no extension is provided by the dev
// DefaultTemplateDirectory the default directory if empty setted
DefaultTemplateDirectory = "." + utils.PathSeparator + "templates"
// DefaultTemplateExtension the default file extension if empty setted
DefaultTemplateExtension = ".html"
// for conversional, exists on config because are shared:
// NoLayout pass it to the layout option on the context.Render to disable layout for this execution
NoLayout = config.NoLayout
)
type (
// TemplateEngine the interface that all template engines must implement
TemplateEngine interface {
// LoadDirectory builds the templates, usually by directory and extension but these are engine's decisions
LoadDirectory(directory string, extension string) error
// LoadAssets loads the templates by binary
// assetFn is a func which returns bytes, use it to load the templates by binary
// namesFn returns the template filenames
LoadAssets(virtualDirectory string, virtualExtension string, assetFn func(name string) ([]byte, error), namesFn func() []string) error
// ExecuteWriter finds, execute a template and write its result to the out writer
// options are the optional runtime options can be passed by user and catched by the template engine when render
// an example of this is the "layout" or "gzip" option
ExecuteWriter(out io.Writer, name string, binding interface{}, options ...map[string]interface{}) error
}
// TemplateEngineFuncs is optional interface for the TemplateEngine
// used to insert the Iris' standard funcs, see var 'usedFuncs'
TemplateEngineFuncs interface {
// Funcs should returns the context or the funcs,
// this property is used in order to register the iris' helper funcs
Funcs() map[string]interface{}
}
)
type (
// TemplateFuncs is is a helper type for map[string]interface{}
TemplateFuncs map[string]interface{}
// RenderOptions is a helper type for the optional runtime options can be passed by user and catched by the template engine when render
// an example of this is the "layout" option
RenderOptions map[string]interface{}
)
// IsFree returns true if a function can be inserted to this map
// return false if this key is already used by Iris
func (t TemplateFuncs) IsFree(key string) bool {
for i := range builtinFuncs {
if builtinFuncs[i] == key {
return false
}
}
return true
}
type (
// TemplateEngineLocation contains the funcs to set the location for the templates by directory or by binary
TemplateEngineLocation struct {
directory string
extension string
assetFn func(name string) ([]byte, error)
namesFn func() []string
}
// TemplateEngineBinaryLocation called after TemplateEngineLocation's Directory, used when files are distrubuted inside the app executable
TemplateEngineBinaryLocation struct {
location *TemplateEngineLocation
}
)
// Directory sets the directory to load from
// returns the Binary location which is optional
func (t *TemplateEngineLocation) Directory(dir string, fileExtension string) TemplateEngineBinaryLocation {
t.directory = dir
t.extension = fileExtension
return TemplateEngineBinaryLocation{location: t}
}
// Binary sets the asset(s) and asssets names to load from, works with Directory
func (t *TemplateEngineBinaryLocation) Binary(assetFn func(name string) ([]byte, error), namesFn func() []string) {
t.location.assetFn = assetFn
t.location.namesFn = namesFn
// if extension is not static(setted by .Directory)
if t.location.extension == "" {
if names := namesFn(); len(names) > 0 {
t.location.extension = filepath.Ext(names[0]) // we need the extension to get the correct template engine on the Render method
}
}
}
func (t *TemplateEngineLocation) isBinary() bool {
return t.assetFn != nil && t.namesFn != nil
}
// TemplateEngineWrapper is the wrapper of a template engine
type TemplateEngineWrapper struct {
TemplateEngine
location *TemplateEngineLocation
buffer *utils.BufferPool
gzipWriterPool sync.Pool
reload bool
combiledContentType string
}
var (
errMissingDirectoryOrAssets = errors.New("Missing Directory or Assets by binary for the template engine!")
errNoTemplateEngineForExt = errors.New("No template engine found to manage '%s' extensions")
)
func (t *TemplateEngineWrapper) load() error {
if t.location.isBinary() {
t.LoadAssets(t.location.directory, t.location.extension, t.location.assetFn, t.location.namesFn)
} else if t.location.directory != "" {
t.LoadDirectory(t.location.directory, t.location.extension)
} else {
return errMissingDirectoryOrAssets.Return()
}
return nil
}
// Execute execute a template and write its result to the context's body
// options are the optional runtime options can be passed by user and catched by the template engine when render
// an example of this is the "layout"
// note that gzip option is an iris dynamic option which exists for all template engines
func (t *TemplateEngineWrapper) Execute(ctx *Context, filename string, binding interface{}, options ...map[string]interface{}) (err error) {
if t == nil {
//file extension, but no template engine registered, this caused by context, and TemplateEngines. GetBy
return errNoTemplateEngineForExt.Format(filepath.Ext(filename))
}
if t.reload {
if err = t.load(); err != nil {
return
}
}
// we do all these because we don't want to initialize a new map for each execution...
gzipEnabled := false
if len(options) > 0 {
gzipOpt := options[0]["gzip"] // we only need that, so don't create new map to keep the options.
if b, isBool := gzipOpt.(bool); isBool {
gzipEnabled = b
}
}
ctxLayout := ctx.GetString(config.TemplateLayoutContextKey)
if ctxLayout != "" {
if len(options) > 0 {
options[0]["layout"] = ctxLayout
} else {
options = []map[string]interface{}{map[string]interface{}{"layout": ctxLayout}}
}
}
var out io.Writer
if gzipEnabled {
ctx.Response.Header.Add("Content-Encoding", "gzip")
gzipWriter := t.gzipWriterPool.Get().(*gzip.Writer)
gzipWriter.Reset(ctx.Response.BodyWriter())
defer gzipWriter.Close()
defer t.gzipWriterPool.Put(gzipWriter)
out = gzipWriter
} else {
out = ctx.Response.BodyWriter()
}
ctx.SetHeader("Content-Type", t.combiledContentType)
return t.ExecuteWriter(out, filename, binding, options...)
}
// ExecuteToString executes a template from a specific template engine and returns its contents result as string, it doesn't renders
func (t *TemplateEngineWrapper) ExecuteToString(filename string, binding interface{}, opt ...map[string]interface{}) (result string, err error) {
if t == nil {
//file extension, but no template engine registered, this caused by context, and TemplateEngines. GetBy
return "", errNoTemplateEngineForExt.Format(filepath.Ext(filename))
}
if t.reload {
if err = t.load(); err != nil {
return
}
}
out := t.buffer.Get()
defer t.buffer.Put(out)
err = t.ExecuteWriter(out, filename, binding, opt...)
if err == nil {
result = out.String()
}
return
}
// TemplateEngines is the container and manager of the template engines
type TemplateEngines struct {
engines []*TemplateEngineWrapper
helpers map[string]interface{}
reload bool
}
func (t *TemplateEngines) setReload(b bool) {
t.reload = b
}
// GetBy receives a filename, gets its extension and returns the template engine responsible for that file extension
func (t *TemplateEngines) GetBy(filename string) *TemplateEngineWrapper {
extension := filepath.Ext(filename)
for i, n := 0, len(t.engines); i < n; i++ {
e := t.engines[i]
if e.location.extension == extension {
return e
}
}
return nil
}
// Add adds but not loads a template engine
func (t *TemplateEngines) Add(e TemplateEngine) *TemplateEngineLocation {
location := &TemplateEngineLocation{}
// add the iris helper funcs
if funcer, ok := e.(TemplateEngineFuncs); ok {
if funcer.Funcs() != nil {
for k, v := range t.helpers {
funcer.Funcs()[k] = v
}
}
}
tmplEngine := &TemplateEngineWrapper{
TemplateEngine: e,
location: location,
buffer: utils.NewBufferPool(20),
gzipWriterPool: sync.Pool{New: func() interface{} {
return &gzip.Writer{}
}},
reload: t.reload,
combiledContentType: config.ContentTypeHTML + "; " + config.Charset,
}
t.engines = append(t.engines, tmplEngine)
return location
}
// loadAll loads all templates using all template engines, returns the first error
// called on iris' initialize
func (t *TemplateEngines) loadAll() error {
for i, n := 0, len(t.engines); i < n; i++ {
e := t.engines[i]
if e.location.directory == "" {
e.location.directory = DefaultTemplateDirectory // the defualt dir ./templates
}
if e.location.extension == "" {
e.location.extension = DefaultTemplateExtension // the default file ext .html
}
if err := e.load(); err != nil {
return err
}
}
return nil
}

View File

@ -1,9 +1,9 @@
package websocket package websocket
import ( import (
"github.com/iris-contrib/logger"
"github.com/kataras/iris/config" "github.com/kataras/iris/config"
"github.com/kataras/iris/context" "github.com/kataras/iris/context"
"github.com/kataras/iris/logger"
) )
// to avoid the import cycle to /kataras/iris. The ws package is used inside iris' station configuration // to avoid the import cycle to /kataras/iris. The ws package is used inside iris' station configuration