Skip to content

Commit

Permalink
New mvc.IgnoreEmbedded option to solve #2103
Browse files Browse the repository at this point in the history
  • Loading branch information
kataras committed Mar 19, 2023
1 parent 0954986 commit 34387a4
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 3 deletions.
30 changes: 30 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,36 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene

Change applies to `master` branch.

- Add `mvc.IgnoreEmbedded` option to handle [#2103](https://github.com/kataras/iris/issues/2103). Example Code:

```go
func configure(m *mvc.Application) {
m.Router.Use(cacheHandler)
m.Handle(&exampleController{
timeFormat: "Mon, Jan 02 2006 15:04:05",
}, mvc.IgnoreEmbedded /* BaseController.GetDoSomething will not be parsed at all */)
}

type BaseController struct {
Ctx iris.Context
}

func (c *BaseController) GetDoSomething(i interface{}) error {
return nil
}

type exampleController struct {
BaseController

timeFormat string
}

func (c *exampleController) Get() string {
now := time.Now().Format(c.timeFormat)
return "last time executed without cache: " + now
}
```

- Add `LoadKV` method on `Iris.Application.I18N` instance. It should be used when no locale files are available. It loads locales via pure Go Map (or database decoded values).

- Remove [ace](https://github.com/eknkc/amber) template parser support, as it was discontinued by its author more than five years ago.
Expand Down
2 changes: 1 addition & 1 deletion README_ZH_HANS.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ http://localhost:8080/books

</details>

[![run in the browser](https://img.shields.io/badge/Run-in%20the%20Browser-348798.svg?style=for-the-badge&logo=repl.it)](https://bit.ly/2YJeSZe)
[![run in the browser](https://img.shields.io/badge/Run-in%20the%20Browser-348798.svg?style=for-the-badge&logo=repl.it)](https://replit.com/@kataras/Iris-Hello-World-v1220?v=1)

Iris 有完整且详尽的 **[使用文档](https://www.iris-go.com/#ebookDonateForm)** ,让您可以轻松地使用此框架。

Expand Down
2 changes: 1 addition & 1 deletion _examples/mvc/middleware/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func configure(m *mvc.Application) {
m.Router.Use(cacheHandler)
m.Handle(&exampleController{
timeFormat: "Mon, Jan 02 2006 15:04:05",
})
} /* ,mvc.IgnoreEmbedded --- Can be used to ignore any embedded struct method handlers */)
}

type exampleController struct {
Expand Down
73 changes: 72 additions & 1 deletion mvc/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ var (
_ AfterActivation = (*ControllerActivator)(nil)
)

// IgnoreEmbeddedControllers is a global variable which indicates whether
// the controller's method parser should skip converting embedded struct's methods to http handlers.
//
// If no global use is necessary, developers can do the same for individual controllers
// through the `IgnoreEmbedded` Controller Option on `mvc.Application.Handle` method.
//
// Defaults to false.
var IgnoreEmbeddedControllers = false

// ControllerActivator returns a new controller type info description.
// Its functionality can be overridden by the end-dev.
type ControllerActivator struct {
Expand All @@ -78,6 +87,8 @@ type ControllerActivator struct {
// End-devs can change some properties of the *Route on the `BeforeActivator` by using the
// `GetRoute/GetRoutes(functionName)`.
routes map[string][]*router.Route

skipMethodNames []string
// BeginHandlers is a slice of middleware for this controller.
// These handlers will be prependend to each one of
// the route that this controller will register(Handle/HandleMany/struct methods)
Expand Down Expand Up @@ -114,7 +125,6 @@ func newControllerActivator(app *Application, controller interface{}) *Controlle
}

typ := reflect.TypeOf(controller)

c := &ControllerActivator{
// give access to the Router to the end-devs if they need it for some reason,
// i.e register done handlers.
Expand All @@ -132,6 +142,10 @@ func newControllerActivator(app *Application, controller interface{}) *Controlle
routes: whatReservedMethods(typ),
}

if IgnoreEmbeddedControllers {
c.SkipEmbeddedMethods()
}

return c
}

Expand All @@ -157,6 +171,43 @@ func whatReservedMethods(typ reflect.Type) map[string][]*router.Route {
return routes
}

func whatEmbeddedMethods(typ reflect.Type) []string {
var embeddedMethodsToIgnore []string
controllerType := typ
if controllerType.Kind() == reflect.Ptr {
controllerType = controllerType.Elem()
}

for i := 0; i < controllerType.NumField(); i++ {
structField := controllerType.Field(i)
structType := structField.Type

if !structField.Anonymous {
continue
}

// var structValuePtr reflect.Value

if structType.Kind() == reflect.Ptr {
// keep both ptr and value instances of the struct so we can ignore all of its methods.
structType = structType.Elem()
// structValuePtr = reflect.ValueOf(reflect.ValueOf(controller).Field(i))
}

if structType.Kind() != reflect.Struct {
continue
}

newEmbeddedStructType := reflect.New(structField.Type).Type()
// let's take its methods and add to methods to ignore from the parent, the controller itself.
for j := 0; j < newEmbeddedStructType.NumMethod(); j++ {
embeddedMethodName := newEmbeddedStructType.Method(j).Name
embeddedMethodsToIgnore = append(embeddedMethodsToIgnore, embeddedMethodName)
}
}
return embeddedMethodsToIgnore
}

// Name returns the full name of the controller, its package name + the type name.
// Can used at both `BeforeActivation` and `AfterActivation`.
func (c *ControllerActivator) Name() string {
Expand All @@ -168,6 +219,20 @@ func (c *ControllerActivator) RelName() string {
return strings.TrimPrefix(c.fullName, "main.")
}

// SkipMethods can be used to individually skip one or more controller's method handlers.
func (c *ControllerActivator) SkipMethods(methodNames ...string) {
c.skipMethodNames = append(c.skipMethodNames, methodNames...)
}

// SkipEmbeddedMethods should be ran before controller parsing.
// It skips all embedded struct's methods conversation to http handlers.
//
// See https://github.com/kataras/iris/issues/2103 for more.
func (c *ControllerActivator) SkipEmbeddedMethods() {
methodsToIgnore := whatEmbeddedMethods(c.Type)
c.SkipMethods(methodsToIgnore...)
}

// Router is the standard Iris router's public API.
// With this you can register middleware, view layouts, subdomains, serve static files
// and even add custom standard iris handlers as normally.
Expand Down Expand Up @@ -259,6 +324,12 @@ func (c *ControllerActivator) isReservedMethod(name string) bool {
}
}

for _, methodName := range c.skipMethodNames {
if methodName == name {
return true
}
}

return false
}

Expand Down
24 changes: 24 additions & 0 deletions mvc/controller_method_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,45 @@ func newMethodLexer(s string) *methodLexer {
return l
}

/*
var allowedCapitalWords = map[string]struct{}{
"ID": {},
"JSON": {},
}
*/

func (l *methodLexer) reset(s string) {
l.cur = -1
var words []string
if s != "" {
end := len(s)
start := -1

// outter:
for i, n := 0, end; i < n; i++ {
c := rune(s[i])
if unicode.IsUpper(c) {
// it doesn't count the last uppercase
if start != -1 {
/*
for allowedCapitalWord := range allowedCapitalWords {
capitalWordEnd := i + len(allowedCapitalWord) // takes last char too, e.g. ReadJSON, we need the JSON.
if len(s) >= capitalWordEnd {
word := s[i:capitalWordEnd]
if word == allowedCapitalWord {
words = append(words, word)
i = capitalWordEnd
start = i
continue outter
}
}
}
*/

end = i
words = append(words, s[start:end])
}

start = i
continue
}
Expand Down
7 changes: 7 additions & 0 deletions mvc/mvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,13 @@ func (opt OptionFunc) Apply(c *ControllerActivator) {
opt(c)
}

// IgnoreEmbedded is an Option which can be used to ignore all embedded struct's method handlers.
//
// For global affect, set the `IgnoreEmbeddedControllers` package-level variable to true.
var IgnoreEmbedded OptionFunc = func(c *ControllerActivator) {
c.SkipEmbeddedMethods()
}

// Handle serves a controller for the current mvc application's Router.
// It accept any custom struct which its functions will be transformed
// to routes.
Expand Down

0 comments on commit 34387a4

Please sign in to comment.