1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-22 20:37:05 +00:00

Update sessions/sessiondb/badger to its latest version and SessionController is able to initialize itself if session manager is not provided by the caller-dev.

Changes:

Update the badger database using transactions, as supported from yesterday via: 06242925c2

Add a custom `OnActivate` via mvc/activator/activate_listener.go, this can be used to perform any custom actions when the app registers the supported Controllers. See mvc/session_controller.go for the excellent use case.

errors.Reporter.AddErr returns true if the error is added to the stack, otherwise false


Former-commit-id: c896a2d186a4315643f3c5fdb4325f7ee48a9e0a
This commit is contained in:
Gerasimos (Makis) Maropoulos
2017-10-06 01:19:10 +03:00
parent 6925ed9b33
commit 4fb78bbcd9
17 changed files with 232 additions and 70 deletions

View File

@@ -0,0 +1,78 @@
package activator
import (
"reflect"
)
// CallOnActivate simply calls the "controller"'s `OnActivate(*ActivatePayload)` function,
// if any.
//
// Look `activator.go#Register` and `ActivateListener` for more.
func CallOnActivate(controller interface{},
bindValues *[]interface{}, registerFunc RegisterFunc) {
if ac, ok := controller.(ActivateListener); ok {
p := &ActivatePayload{
BindValues: bindValues,
Handle: registerFunc,
}
ac.OnActivate(p)
}
}
// ActivateListener is an interface which should be declared
// on a Controller which needs to register or change the bind values
// that the caller-"user" has been passed to; via the `app.Controller`.
// If that interface is completed by a controller
// then the `OnActivate` function will be called ONCE, NOT in every request
// but ONCE at the application's lifecycle.
type ActivateListener interface {
// OnActivate accepts a pointer to the `ActivatePayload`.
//
// The `Controller` can make use of the `OnActivate` function
// to register custom routes
// or modify the provided values that will be binded to the
// controller later on.
//
// Look `ActivatePayload` for more.
OnActivate(*ActivatePayload)
}
// ActivatePayload contains the necessary information and the ability
// to alt a controller's registration options, i.e the binder.
//
// With `ActivatePayload` the `Controller` can register custom routes
// or modify the provided values that will be binded to the
// controller later on.
type ActivatePayload struct {
BindValues *[]interface{}
Handle RegisterFunc
}
// EnsureBindValue will make sure that this "bindValue"
// will be registered to the controller's binder
// if its type is not already passed by the caller..
//
// For example, on `SessionController` it looks if *sessions.Sessions
// has been binded from the caller and if not then the "bindValue"
// will be binded and used as a default sessions manager instead.
//
// At general, if the caller has already provided a value with the same Type
// then the "bindValue" will be ignored and not be added to the controller's bind values.
//
// Returns true if the caller has NOT already provided a value with the same Type
// and "bindValue" is NOT ignored therefore is appended to the controller's bind values.
func (i *ActivatePayload) EnsureBindValue(bindValue interface{}) bool {
valueTyp := reflect.TypeOf(bindValue)
localBindValues := *i.BindValues
for _, bindedValue := range localBindValues {
// type already exists, remember: binding here is per-type.
if reflect.TypeOf(bindedValue) == valueTyp {
return false
}
}
*i.BindValues = append(localBindValues, bindValue)
return true
}

View File

@@ -65,7 +65,6 @@ type BaseController interface {
// ActivateController returns a new controller type info description.
func ActivateController(base BaseController, bindValues []interface{}) (TController, error) {
// get and save the type.
typ := reflect.TypeOf(base)
if typ.Kind() != reflect.Ptr {
@@ -202,6 +201,8 @@ func RegisterMethodHandlers(t TController, registerFunc RegisterFunc) {
func Register(controller BaseController, bindValues []interface{},
registerFunc RegisterFunc) error {
CallOnActivate(controller, &bindValues, registerFunc)
t, err := ActivateController(controller, bindValues)
if err != nil {
return err

View File

@@ -7,6 +7,7 @@ import (
"github.com/kataras/iris"
"github.com/kataras/iris/context"
"github.com/kataras/iris/mvc"
"github.com/kataras/iris/mvc/activator"
"github.com/kataras/iris/core/router"
"github.com/kataras/iris/httptest"
@@ -493,3 +494,33 @@ func TestControllerRelPathFromFunc(t *testing.T) {
e.GET("/anything/here").Expect().Status(httptest.StatusOK).
Body().Equal("GET:/anything/here")
}
type testControllerActivateListener struct {
mvc.Controller
TitlePointer *testBindType
}
func (c *testControllerActivateListener) OnActivate(p *activator.ActivatePayload) {
p.EnsureBindValue(&testBindType{
title: "default title",
})
}
func (c *testControllerActivateListener) Get() {
c.Text = c.TitlePointer.title
}
func TestControllerActivateListener(t *testing.T) {
app := iris.New()
app.Controller("/", new(testControllerActivateListener))
app.Controller("/manual", new(testControllerActivateListener), &testBindType{
title: "my title",
})
e := httptest.New(t, app)
e.GET("/").Expect().Status(httptest.StatusOK).
Body().Equal("default title")
e.GET("/manual").Expect().Status(httptest.StatusOK).
Body().Equal("my title")
}

19
mvc/go19.go Normal file
View File

@@ -0,0 +1,19 @@
// +build go1.9
package mvc
import (
"github.com/kataras/iris/mvc/activator"
)
// ActivatePayload contains the necessary information and the ability
// to alt a controller's registration options, i.e the binder.
//
// With `ActivatePayload` the `Controller` can register custom routes
// or modify the provided values that will be binded to the
// controller later on.
//
// Look the `mvc/activator#ActivatePayload` for its implementation.
//
// A shortcut for the `mvc/activator#ActivatePayload`, useful when `OnActivate` is being used.
type ActivatePayload = activator.ActivatePayload

View File

@@ -2,9 +2,14 @@ package mvc
import (
"github.com/kataras/iris/context"
"github.com/kataras/iris/mvc/activator"
"github.com/kataras/iris/sessions"
"github.com/kataras/golog"
)
var defaultManager = sessions.New(sessions.Config{})
// SessionController is a simple `Controller` implementation
// which requires a binded session manager in order to give
// direct access to the current client's session via its `Session` field.
@@ -15,21 +20,27 @@ type SessionController struct {
Session *sessions.Session
}
var managerMissing = "MVC SessionController: session manager field is nil, you have to bind it to a *sessions.Sessions"
// OnActivate called, once per application lifecycle NOT request,
// every single time the dev registers a specific SessionController-based controller.
// It makes sure that its "Manager" field is filled
// even if the caller didn't provide any sessions manager via the `app.Controller` function.
func (s *SessionController) OnActivate(p *activator.ActivatePayload) {
if p.EnsureBindValue(defaultManager) {
golog.Warnf(`MVC SessionController: couldn't find any "*sessions.Sessions" bindable value to fill the "Manager" field,
therefore this controller is using the default sessions manager instead.
Please refer to the documentation to learn how you can provide the session manager`)
}
}
// BeginRequest calls the Controller's BeginRequest
// and tries to initialize the current user's Session.
func (s *SessionController) BeginRequest(ctx context.Context) {
s.Controller.BeginRequest(ctx)
if s.Manager == nil {
ctx.Application().Logger().Errorf(managerMissing)
ctx.Application().Logger().Errorf(`MVC SessionController: sessions manager is nil, report this as a bug
because the SessionController should predict this on its activation state and use a default one automatically`)
return
}
s.Session = s.Manager.Start(ctx)
}
/* TODO:
Maybe add struct tags on `binder` for required binded values
in order to log error if some of the bindings are missing or leave that to the end-developers?
*/