Update to 5.0.4 - Read HISTORY.md

This commit is contained in:
Gerasimos (Makis) Maropoulos 2016-12-12 12:18:59 +02:00
parent 424ede7258
commit 309b037e3b
11 changed files with 204 additions and 240 deletions

View File

@ -1,4 +1,4 @@
- Version : **5.0.1**
- Version : **5.0.4**
- Operating System:

View File

@ -2,6 +2,44 @@
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`.
## 5.0.3 -> 5.0.4
The use of `iris.BodyDecoder` as a custom decoder that you can implement to a type in order to be used as the decoder/binder for the request body and override the json.Unmarshal(`context.ReadJSON`) or xml.Unmarshal(`context.ReadXML`) was very useful and gave you some kind of **per-type-binder** extensibility.
**NEW** `context.UnmarshalBody`: **Per-service-binder**. Side by side with the `iris.BodyDecoder`. We now have a second way to pass a custom `Unmarshaler` to override the `json.Unmarshal` and `xml.Unmarshal`.
If the object doesn't implements the `iris.BodyDecoder` but you still want to implement your own algorithm to parse []byte as an 'object' instead of the iris' defaults.
```go
type Unmarshaler interface {
Unmarshal(data []byte, v interface{}) error
}
```
`context.ReadJSON & context.ReadXML` have been also refactored to work with this interface and the new `context.DeodeBody` function, look:
```go
// ReadJSON reads JSON from request's body
// and binds it to a value of any json-valid type
func (ctx *Context) ReadJSON(jsonObject interface{}) error {
return ctx.UnmarshalBody(jsonObject, UnmarshalerFunc(json.Unmarshal))
}
// ReadXML reads XML from request's body
// and binds it to a value of any xml-valid type
func (ctx *Context) ReadXML(xmlObject interface{}) error {
return ctx.UnmarshalBody(xmlObject, UnmarshalerFunc(xml.Unmarshal))
}
```
Both `encoding/json` and `encoding/xml` standard packages have valid `Unmarshal function` so they can be used as `iris.Unmarshaller` (with the help of `iris.UnmarshallerFunc` which just converts the signature to the `iris.Unmarshaller` interface). You only have to implement one function and it will work with any 'object' passed to the `UnmarshalBody` even if the object doesn't implements the `iris.BodyDecoder`.
## 5.0.2 -> 5.0.3
- Fix `https relative redirect paths`, a very old issue, which I just saw, peaceful, again :)

View File

@ -20,7 +20,7 @@
<br/>
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%205.0.3%20-blue.svg?style=flat-square" alt="Releases"></a>
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%205.0.4%20-blue.svg?style=flat-square" alt="Releases"></a>
<a href="https://github.com/iris-contrib/examples"><img src="https://img.shields.io/badge/%20examples-repository-3362c2.svg?style=flat-square" alt="Examples"></a>
@ -36,7 +36,7 @@ ideally suited for <br/> both experienced and novice developers.<br/><br/>
Besides the fact that Iris is faster than any alternatives you may met before, <br/> thanks to its fluent API, <b>you don't have to be an expert to work with it.</b><br/> <br/>
If you're coming from <a href="(https://nodejs.org/en/">Node.js</a> world, this is the <a href="https://github.com/expressjs/express">expressjs</a> alternative for the <a href="https://golang.org">Go Programming Language.</a>
If you're coming from <a href="https://nodejs.org/en/">Node.js</a> world, this is the <a href="https://github.com/expressjs/express">expressjs</a> alternative for the <a href="https://golang.org">Go Programming Language.</a>
<br/>
<br/>
@ -873,7 +873,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
Versioning
------------
Current: **v5.0.3**
Current: **v5.0.4**
Stable: **[v4 LTS](https://github.com/kataras/iris/tree/4.0.0#versioning)**

View File

@ -617,7 +617,6 @@ var (
)
var (
universe time.Time // 0001-01-01 00:00:00 +0000 UTC
// CookieExpireNever the default cookie's life for sessions, unlimited (23 years)
CookieExpireNever = time.Now().AddDate(23, 0, 0)
)

View File

@ -26,8 +26,6 @@ import (
)
const (
// DefaultUserAgent default to 'iris' but it is not used anywhere yet
defaultUserAgent = "iris"
// ContentType represents the header["Content-Type"]
contentType = "Content-Type"
// ContentLength represents the header["Content-Length"]
@ -80,10 +78,7 @@ const (
var (
errTemplateExecute = errors.New("Unable to execute a template. Trace: %s")
errFlashNotFound = errors.New("Unable to get flash message. Trace: Cookie does not exists")
errSessionNil = errors.New("Unable to set session, Config().Session.Provider is nil, please refer to the docs!")
errNoForm = errors.New("Request has no any valid form")
errWriteJSON = errors.New("Before JSON be written to the body, JSON Encoder returned an error. Trace: %s")
errRenderMarshalled = errors.New("Before +type Rendering, MarshalIndent returned an error. Trace: %s")
errReadBody = errors.New("While trying to read %s from the request body. Trace %s")
errServeContent = errors.New("While trying to serve content to the client. Trace %s")
)
@ -282,22 +277,27 @@ func (ctx *Context) FormValues(name string) []string {
// PostValuesAll returns all post data values with their keys
// multipart, form data, get & post query arguments
func (ctx *Context) PostValuesAll() (valuesAll map[string][]string) {
reqCtx := ctx.RequestCtx
valuesAll = make(map[string][]string)
//
// NOTE: A check for nil is necessary for zero results
func (ctx *Context) PostValuesAll() map[string][]string {
// first check if we have multipart form
multipartForm, err := reqCtx.MultipartForm()
multipartForm, err := ctx.MultipartForm()
if err == nil {
//we have multipart form
return multipartForm.Value
}
// if no multipart and post arguments ( means normal form)
if reqCtx.PostArgs().Len() == 0 && reqCtx.QueryArgs().Len() == 0 {
return // no found
postArgs := ctx.PostArgs()
queryArgs := ctx.QueryArgs()
len := postArgs.Len() + queryArgs.Len()
if len == 0 {
return nil // nothing found
}
reqCtx.PostArgs().VisitAll(func(k []byte, v []byte) {
valuesAll := make(map[string][]string, len)
visitor := func(k []byte, v []byte) {
key := string(k)
value := string(v)
// for slices
@ -306,21 +306,11 @@ func (ctx *Context) PostValuesAll() (valuesAll map[string][]string) {
} else {
valuesAll[key] = []string{value}
}
})
reqCtx.QueryArgs().VisitAll(func(k []byte, v []byte) {
key := string(k)
value := string(v)
// for slices
if valuesAll[key] != nil {
valuesAll[key] = append(valuesAll[key], value)
} else {
valuesAll[key] = []string{value}
}
})
return
postArgs.VisitAll(visitor)
queryArgs.VisitAll(visitor)
return valuesAll
}
// PostValues returns the post data values as []string of a single key/name
@ -370,90 +360,61 @@ type BodyDecoder interface {
Decode(data []byte) error
}
// ReadJSON reads JSON from request's body
// Unmarshaler is the interface implemented by types that can unmarshal any raw data
// TIP INFO: Any v object which implements the BodyDecoder can be override the unmarshaler
type Unmarshaler interface {
Unmarshal(data []byte, v interface{}) error
}
// UnmarshalerFunc a shortcut for the Unmarshaler interface
//
// See 'Unmarshaler' and 'BodyDecoder' for more
type UnmarshalerFunc func(data []byte, v interface{}) error
// Unmarshal parses the X-encoded data and stores the result in the value pointed to by v.
// Unmarshal uses the inverse of the encodings that Marshal uses, allocating maps,
// slices, and pointers as necessary.
func (u UnmarshalerFunc) Unmarshal(data []byte, v interface{}) error {
return u(data, v)
}
// UnmarshalBody reads the request's body and binds it to a value or pointer of any type
// Examples of usage: context.ReadJSON, context.ReadXML
func (ctx *Context) UnmarshalBody(v interface{}, unmarshaler Unmarshaler) error {
rawData := ctx.Request.Body()
// check if the v contains its own decode
// in this case the v should be a pointer also,
// but this is up to the user's custom Decode implementation*
//
// See 'BodyDecoder' for more
if decoder, isDecoder := v.(BodyDecoder); isDecoder {
return decoder.Decode(rawData)
}
// check if v is already a pointer, if yes then pass as it's
if reflect.TypeOf(v).Kind() == reflect.Ptr {
return unmarshaler.Unmarshal(rawData, v)
}
// finally, if the v doesn't contains a self-body decoder and it's not a pointer
// use the custom unmarshaler to bind the body
return unmarshaler.Unmarshal(rawData, &v)
}
// ReadJSON reads JSON from request's body and binds it to a value of any json-valid type
func (ctx *Context) ReadJSON(jsonObject interface{}) error {
rawData := ctx.Request.Body()
// check if the jsonObject contains its own decode
// in this case the jsonObject should be a pointer also,
// but this is up to the user's custom Decode implementation*
//
// See 'BodyDecoder' for more
if decoder, isDecoder := jsonObject.(BodyDecoder); isDecoder {
return decoder.Decode(rawData)
return ctx.UnmarshalBody(jsonObject, UnmarshalerFunc(json.Unmarshal))
}
// check if jsonObject is already a pointer, if yes then pass as it's
if reflect.TypeOf(jsonObject).Kind() == reflect.Ptr {
return json.Unmarshal(rawData, jsonObject)
}
// finally, if the jsonObject doesn't contains a self-body decoder and it's not a pointer
return json.Unmarshal(rawData, &jsonObject)
}
// ReadXML reads XML from request's body
// ReadXML reads XML from request's body and binds it to a value of any xml-valid type
func (ctx *Context) ReadXML(xmlObject interface{}) error {
rawData := ctx.Request.Body()
// check if the xmlObject contains its own decode
// in this case the jsonObject should be a pointer also,
// but this is up to the user's custom Decode implementation*
//
// See 'BodyDecoder' for more
if decoder, isDecoder := xmlObject.(BodyDecoder); isDecoder {
return decoder.Decode(rawData)
}
// check if xmlObject is already a pointer, if yes then pass as it's
if reflect.TypeOf(xmlObject).Kind() == reflect.Ptr {
return xml.Unmarshal(rawData, xmlObject)
}
// finally, if the xmlObject doesn't contains a self-body decoder and it's not a pointer
return xml.Unmarshal(rawData, &xmlObject)
return ctx.UnmarshalBody(xmlObject, UnmarshalerFunc(xml.Unmarshal))
}
// ReadForm binds the formObject with the form data
// it supports any kind of struct
func (ctx *Context) ReadForm(formObject interface{}) error {
reqCtx := ctx.RequestCtx
// first check if we have multipart form
multipartForm, err := reqCtx.MultipartForm()
if err == nil {
//we have multipart form
return errReadBody.With(formBinder.Decode(multipartForm.Value, formObject))
}
// if no multipart and post arguments ( means normal form)
if reqCtx.PostArgs().Len() == 0 && reqCtx.QueryArgs().Len() == 0 {
return errReadBody.With(errNoForm)
}
form := make(map[string][]string, reqCtx.PostArgs().Len()+reqCtx.QueryArgs().Len())
reqCtx.PostArgs().VisitAll(func(k []byte, v []byte) {
key := string(k)
value := string(v)
// for slices
if form[key] != nil {
form[key] = append(form[key], value)
} else {
form[key] = []string{value}
}
})
reqCtx.QueryArgs().VisitAll(func(k []byte, v []byte) {
key := string(k)
value := string(v)
// for slices
if form[key] != nil {
form[key] = append(form[key], value)
} else {
form[key] = []string{value}
}
})
return errReadBody.With(formBinder.Decode(form, formObject))
return errReadBody.With(formBinder.Decode(ctx.PostValuesAll(), formObject))
}
/* Response */
@ -699,6 +660,34 @@ func (ctx *Context) Markdown(status int, markdown string) {
ctx.HTML(status, ctx.MarkdownString(markdown))
}
// staticCachePassed checks the IfModifiedSince header and
// returns true if (client-side) duration has expired
func (ctx *Context) staticCachePassed(modtime time.Time) bool {
if t, err := time.Parse(ctx.framework.Config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && modtime.Before(t.Add(StaticCacheDuration)) {
ctx.Response.Header.Del(contentType)
ctx.Response.Header.Del(contentLength)
ctx.SetStatusCode(StatusNotModified)
return true
}
return false
}
// SetClientCachedBody like SetBody but it sends with an expiration datetime
// which is managed by the client-side (all major browsers supports this feature)
func (ctx *Context) SetClientCachedBody(status int, bodyContent []byte, cType string, modtime time.Time) {
if ctx.staticCachePassed(modtime) {
return
}
modtimeFormatted := modtime.UTC().Format(ctx.framework.Config.TimeFormat)
ctx.Response.Header.Set(contentType, cType)
ctx.Response.Header.Set(lastModified, modtimeFormatted)
ctx.SetStatusCode(status)
ctx.Response.SetBody(bodyContent)
}
// ServeContent serves content, headers are autoset
// receives three parameters, it's low-level function, instead you can use .ServeFile(string,bool)/SendFile(string,string)
//

View File

@ -313,8 +313,7 @@ const (
// matchEverythingByte is just a byte of '*" rune/char
matchEverythingByte = byte('*')
isStatic entryCase = iota
isRoot
isRoot entryCase = iota
hasParams
matchEverything
)

146
iris.go
View File

@ -80,7 +80,7 @@ const (
// IsLongTermSupport flag is true when the below version number is a long-term-support version
IsLongTermSupport = false
// Version is the current version number of the Iris web framework
Version = "5.0.3"
Version = "5.0.4"
banner = ` _____ _
|_ _| (_)
@ -900,6 +900,28 @@ func Path(routeName string, args ...interface{}) string {
return Default.Path(routeName, args...)
}
func joinPathArguments(args ...interface{}) []interface{} {
arguments := args[0:]
for i, v := range arguments {
if arr, ok := v.([]string); ok {
if len(arr) > 0 {
interfaceArr := make([]interface{}, len(arr))
for j, sv := range arr {
interfaceArr[j] = sv
}
// replace the current slice
// with the first string element (always as interface{})
arguments[i] = interfaceArr[0]
// append the rest of them to the slice itself
// the range is not affected by these things in go,
// so we are safe to do it.
arguments = append(args, interfaceArr[1:]...)
}
}
}
return arguments
}
// Path used to check arguments with the route's named parameters and return the correct url
// if parse failed returns empty string
func (s *Framework) Path(routeName string, args ...interface{}) string {
@ -950,22 +972,7 @@ func (s *Framework) Path(routeName string, args ...interface{}) string {
return ""
}
arguments := args[0:]
// check for arrays
for i, v := range arguments {
if arr, ok := v.([]string); ok {
if len(arr) > 0 {
interfaceArr := make([]interface{}, len(arr))
for j, sv := range arr {
interfaceArr[j] = sv
}
arguments[i] = interfaceArr[0]
arguments = append(arguments, interfaceArr[1:]...)
}
}
}
arguments := joinPathArguments(args...)
return fmt.Sprintf(r.formattedPath, arguments...)
}
@ -1019,22 +1026,7 @@ func (s *Framework) URL(routeName string, args ...interface{}) (url string) {
scheme := s.Config.VScheme // if s.Config.VScheme was setted, that will be used instead of the real, in order to make easy to run behind nginx
host := s.Config.VHost // if s.Config.VHost was setted, that will be used instead of the real, in order to make easy to run behind nginx
arguments := args[0:]
// join arrays as arguments
for i, v := range arguments {
if arr, ok := v.([]string); ok {
if len(arr) > 0 {
interfaceArr := make([]interface{}, len(arr))
for j, sv := range arr {
interfaceArr[j] = sv
}
arguments[i] = interfaceArr[0]
arguments = append(arguments, interfaceArr[1:]...)
}
}
}
arguments := joinPathArguments(args...)
// if it's dynamic subdomain then the first argument is the subdomain part
if r.subdomain == dynamicSubdomainIndicator {
@ -1741,6 +1733,21 @@ func Static(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
return Default.Static(reqPath, systemPath, stripSlashes)
}
// if / then returns /*wildcard or /something then /something/*wildcard
// if empty then returns /*wildcard too
func validateWildcard(reqPath string, paramName string) string {
if reqPath[len(reqPath)-1] != slashByte {
reqPath += slash
}
reqPath += "*" + paramName
return reqPath
}
func (api *muxAPI) registerResourceRoute(reqPath string, h HandlerFunc) RouteNameFunc {
api.Head(reqPath, h)
return api.Get(reqPath, h)
}
// Static registers a route which serves a system directory
// this doesn't generates an index page which list all files
// no compression is used also, for these features look at StaticFS func
@ -1752,14 +1759,9 @@ func Static(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
// * stripSlashes = 1, original path: "/foo/bar", result: "/bar"
// * stripSlashes = 2, original path: "/foo/bar", result: ""
func (api *muxAPI) Static(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
if reqPath[len(reqPath)-1] != slashByte { // if / then /*filepath, if /something then /something/*filepath
reqPath += slash
}
h := api.StaticHandler(systemPath, stripSlashes, false, false, nil)
api.Head(reqPath+"*filepath", h)
return api.Get(reqPath+"*filepath", h)
reqPath = validateWildcard(reqPath, "filepath")
return api.registerResourceRoute(reqPath, h)
}
// StaticFS registers a route which serves a system directory
@ -1791,13 +1793,9 @@ func StaticFS(reqPath string, systemPath string, stripSlashes int) RouteNameFunc
// * stripSlashes = 1, original path: "/foo/bar", result: "/bar"
// * stripSlashes = 2, original path: "/foo/bar", result: ""
func (api *muxAPI) StaticFS(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
if reqPath[len(reqPath)-1] != slashByte {
reqPath += slash
}
h := api.StaticHandler(systemPath, stripSlashes, true, true, nil)
api.Head(reqPath+"*filepath", h)
return api.Get(reqPath+"*filepath", h)
reqPath = validateWildcard(reqPath, "filepath")
return api.registerResourceRoute(reqPath, h)
}
// StaticWeb same as Static but if index.html exists and request uri is '/' then display the index.html's contents
@ -1824,18 +1822,13 @@ func StaticWeb(reqPath string, systemPath string, stripSlashes int) RouteNameFun
// * if you don't know what to put on stripSlashes just 1
// example: https://github.com/iris-contrib/examples/tree/master/static_web
func (api *muxAPI) StaticWeb(reqPath string, systemPath string, stripSlashes int) RouteNameFunc {
if reqPath[len(reqPath)-1] != slashByte { // if / then /*filepath, if /something then /something/*filepath
reqPath += slash
}
//todo: fs.go
hasIndex := utils.Exists(systemPath + utils.PathSeparator + "index.html")
var indexNames []string
if hasIndex {
indexNames = []string{"index.html"}
}
serveHandler := api.StaticHandler(systemPath, stripSlashes, false, !hasIndex, indexNames) // if not index.html exists then generate index.html which shows the list of files
api.Head(reqPath+"*filepath", serveHandler)
return api.Get(reqPath+"*filepath", serveHandler)
return api.registerResourceRoute(reqPath+"*filepath", serveHandler)
}
// StaticServe serves a directory as web resource
@ -1890,26 +1883,11 @@ func StaticContent(reqPath string, contentType string, content []byte) RouteName
// a good example of this is how the websocket server uses that to auto-register the /iris-ws.js
func (api *muxAPI) StaticContent(reqPath string, cType string, content []byte) RouteNameFunc { // func(string) because we use that on websockets
modtime := time.Now()
modtimeStr := ""
h := func(ctx *Context) {
if modtimeStr == "" {
modtimeStr = modtime.UTC().Format(ctx.framework.Config.TimeFormat)
ctx.SetClientCachedBody(StatusOK, content, cType, modtime)
}
if t, err := time.Parse(ctx.framework.Config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && modtime.Before(t.Add(StaticCacheDuration)) {
ctx.Response.Header.Del(contentType)
ctx.Response.Header.Del(contentLength)
ctx.SetStatusCode(StatusNotModified)
return
}
ctx.Response.Header.Set(contentType, cType)
ctx.Response.Header.Set(lastModified, modtimeStr)
ctx.SetStatusCode(StatusOK)
ctx.Response.SetBody(content)
}
api.Head(reqPath, h)
return api.Get(reqPath, h)
return api.registerResourceRoute(reqPath, h)
}
// StaticEmbedded used when files are distrubuted inside the app executable, using go-bindata mostly
@ -1983,7 +1961,6 @@ func (api *muxAPI) StaticEmbedded(requestPath string, vdir string, assetFn func(
}
modtime := time.Now()
modtimeStr := ""
h := func(ctx *Context) {
reqPath := ctx.Param(paramName)
@ -1994,17 +1971,6 @@ func (api *muxAPI) StaticEmbedded(requestPath string, vdir string, assetFn func(
continue
}
if modtimeStr == "" {
modtimeStr = modtime.UTC().Format(ctx.framework.Config.TimeFormat)
}
if t, err := time.Parse(ctx.framework.Config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && modtime.Before(t.Add(StaticCacheDuration)) {
ctx.Response.Header.Del(contentType)
ctx.Response.Header.Del(contentLength)
ctx.SetStatusCode(StatusNotModified)
return
}
cType := fs.TypeByExtension(path)
fullpath := vdir + path
@ -2014,24 +1980,16 @@ func (api *muxAPI) StaticEmbedded(requestPath string, vdir string, assetFn func(
continue
}
ctx.Response.Header.Set(contentType, cType)
ctx.Response.Header.Set(lastModified, modtimeStr)
ctx.SetStatusCode(StatusOK)
ctx.SetContentType(cType)
ctx.Response.SetBody(buf)
ctx.SetClientCachedBody(StatusOK, buf, cType, modtime)
return
}
// not found
// not found or error
ctx.EmitError(StatusNotFound)
}
api.Head(requestPath, h)
return api.Get(requestPath, h)
return api.registerResourceRoute(requestPath, h)
}
// Favicon serves static favicon
@ -2104,8 +2062,8 @@ func (api *muxAPI) Favicon(favPath string, requestPath ...string) RouteNameFunc
if len(requestPath) > 0 {
reqPath = requestPath[0]
}
api.Head(reqPath, h)
return api.Get(reqPath, h)
return api.registerResourceRoute(reqPath, h)
}
// Layout oerrides the parent template layout with a more specific layout for this Party

View File

@ -7,21 +7,6 @@ import (
"strings"
)
var workingDir string
func getWorkingDir() string {
if workingDir == "" {
errUnableToGetWD := errors.New(Name + ": Unable to get working directory, %s")
// set the current working dir
d, err := os.Getwd()
if err != nil {
panic(errUnableToGetWD.Format(err))
}
workingDir = d
}
return workingDir
}
var goPath string
// returns the (last) gopath+"/src/"

View File

@ -9,7 +9,6 @@ var (
// Name the name of the cmd tool
Name = "Iris Command Line Tool"
app *cli.App
defaultInstallDir string
)
func init() {

View File

@ -127,27 +127,28 @@ func (t testPluginActivationType) Activate(p iris.PluginContainer) error {
return nil
}
// a plugin may contain children plugins too, but,
// if an error happened then all of them are not activated/added to the plugin container
func AddPluginTo(t *testing.T, plugins iris.PluginContainer, plugin iris.Plugin, expectingCount int) {
plugins.Add(plugin)
if plugins.Len() != expectingCount { // 2 because it registeres a second plugin also
t.Fatalf("Expected activated plugins to be: %d but we got: %d", expectingCount, plugins.Len())
}
}
// if any error returned from the Activate plugin's method,
// then this plugin and the plugins it registers should not be registered at all
func TestPluginActivate(t *testing.T) {
iris.ResetDefault()
var plugins = iris.Default.Plugins
myplugin := testPluginActivationType{shouldError: false}
plugins.Add(myplugin)
plugins := iris.Plugins
if plugins.Len() != 2 { // 2 because it registeres a second plugin also
t.Fatalf("Expected activated plugins to be: %d but we got: %d", 0, plugins.Len())
}
}
myValidPluginWithChild := testPluginActivationType{shouldError: false}
AddPluginTo(t, plugins, myValidPluginWithChild, 2) // 2 because its children registered also (its parent is not throwing an error)
// if any error returned from the Activate plugin's method, then this plugin and the plugins it registers should not be registered at all
func TestPluginActivationError(t *testing.T) {
iris.ResetDefault()
var plugins = iris.Default.Plugins
myplugin := testPluginActivationType{shouldError: true}
plugins.Add(myplugin)
if plugins.Len() > 0 {
t.Fatalf("Expected activated plugins to be: %d but we got: %d", 0, plugins.Len())
}
myInvalidPlugin := testPluginActivationType{shouldError: true}
// should stay 2, even if we tried to add a new one,
// because it cancels the registration of its children too (shouldError = true)
AddPluginTo(t, plugins, myInvalidPlugin, 2)
}
func TestPluginEvents(t *testing.T) {

View File

@ -6,10 +6,6 @@ import (
"io"
)
var (
builtinFuncs = [...]string{"url", "urlpath"}
)
const (
// NoLayout to disable layout for a particular template file
NoLayout = template.NoLayout