Skip to content

Commit 02732b5

Browse files
authored
Merge pull request revel#1185 from notzippy/route-wildcard-fix
Fixes wildcards in router files in app from affecting modules or visa…
2 parents 1480004 + 2fefd09 commit 02732b5

File tree

12 files changed

+1078
-482
lines changed

12 files changed

+1078
-482
lines changed

controller.go

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ type Controller struct {
4040
Validation *Validation // Data validation helpers
4141
}
4242

43+
// The map of controllers, controllers are mapped by using the namespace|controller_name as the key
44+
var controllers = make(map[string]*ControllerType)
45+
4346
// NewController returns new controller instance for Request and Response
4447
func NewController(req *Request, resp *Response) *Controller {
4548
return &Controller{
@@ -91,10 +94,19 @@ func (c *Controller) setStatusIfNil(status int) {
9194
//
9295
// This action will render views/Users/ShowUser.html, passing in an extra
9396
// key-value "user": (User).
97+
//
98+
// This is the slower magical version which uses the runtime
99+
// to determine
100+
// 1) Set c.ViewArgs to the arguments passed into this function
101+
// 2) How to call the RenderTemplate by building the following line
102+
// c.RenderTemplate(c.Name + "/" + c.MethodType.Name + "." + c.Request.Format)
103+
//
104+
// If you want your code to run faster it is recommended you add the template values directly
105+
// to the c.ViewArgs and call c.RenderTemplate directly
94106
func (c *Controller) Render(extraViewArgs ...interface{}) Result {
95107
c.setStatusIfNil(http.StatusOK)
96108

97-
// Get the calling function name.
109+
// Get the calling function line number.
98110
_, _, line, ok := runtime.Caller(1)
99111
if !ok {
100112
ERROR.Println("Failed to get Caller information")
@@ -131,7 +143,7 @@ func (c *Controller) RenderTemplate(templatePath string) Result {
131143
}
132144

133145
return &RenderTemplateResult{
134-
Template: template,
146+
Template: template,
135147
ViewArgs: c.ViewArgs,
136148
}
137149
}
@@ -276,11 +288,21 @@ func (c *Controller) Message(message string, args ...interface{}) (value string)
276288
// It sets the following properties: Name, Action, Type, MethodType
277289
func (c *Controller) SetAction(controllerName, methodName string) error {
278290

291+
return c.SetTypeAction(controllerName, methodName, nil)
292+
}
293+
// SetAction sets the assigns the Controller type, sets the action and initializes the controller
294+
func (c *Controller) SetTypeAction(controllerName, methodName string, typeOfController *ControllerType) error {
295+
279296
// Look up the controller and method types.
280-
var ok bool
281-
if c.Type, ok = controllers[strings.ToLower(controllerName)]; !ok {
282-
return errors.New("revel/controller: failed to find controller " + controllerName)
297+
if typeOfController== nil {
298+
if c.Type = ControllerTypeByName(controllerName, anyModule); c.Type == nil {
299+
return errors.New("revel/controller: failed to find controller " + controllerName)
300+
}
301+
} else {
302+
c.Type = typeOfController
283303
}
304+
305+
// Note method name is case insensitive search
284306
if c.MethodType = c.Type.Method(methodName); c.MethodType == nil {
285307
return errors.New("revel/controller: failed to find action " + methodName)
286308
}
@@ -294,6 +316,27 @@ func (c *Controller) SetAction(controllerName, methodName string) error {
294316
return nil
295317
}
296318

319+
func ControllerTypeByName(controllerName string, moduleSource *Module) (c *ControllerType) {
320+
var found bool
321+
if c, found = controllers[controllerName]; !found {
322+
// Backup, passed in controllerName should be in lower case, but may not be
323+
if c, found = controllers[strings.ToLower(controllerName)]; !found {
324+
INFO.Printf("Cannot find controller name '%s' in controllers map ", controllerName)
325+
// Search for the controller by name
326+
for _, cType := range controllers {
327+
testControllerName := strings.ToLower(cType.Type.Name())
328+
if testControllerName == strings.ToLower(controllerName) && (cType.ModuleSource == moduleSource || moduleSource == anyModule) {
329+
WARN.Printf("Matched empty namespace controller for %s to this %s", controllerName, cType.ModuleSource.Name)
330+
c = cType
331+
found = true
332+
break
333+
}
334+
}
335+
}
336+
}
337+
return
338+
}
339+
297340
// This is a helper that initializes (zeros) a new app controller value.
298341
// Specifically, it sets all *revel.Controller embedded types to the provided controller.
299342
// Returns a value representing a pointer to the new app controller.
@@ -364,6 +407,8 @@ func findControllers(appControllerType reflect.Type) (indexes [][]int) {
364407
// Controller registry and types.
365408

366409
type ControllerType struct {
410+
Namespace string // The namespace of the controller
411+
ModuleSource *Module // The module for the controller
367412
Type reflect.Type
368413
Methods []*MethodType
369414
ControllerIndexes [][]int // FieldByIndex to all embedded *Controllers
@@ -381,6 +426,32 @@ type MethodArg struct {
381426
Type reflect.Type
382427
}
383428

429+
// Adds the controller to the controllers map using its namespace, also adds it to the module list of controllers.
430+
// If the controller is in the main application it is added without its namespace as well.
431+
func AddControllerType(moduleSource *Module,controllerType reflect.Type,methods []*MethodType) (newControllerType *ControllerType) {
432+
if moduleSource==nil {
433+
moduleSource = appModule
434+
}
435+
436+
newControllerType = &ControllerType{ModuleSource:moduleSource,Type:controllerType,Methods:methods,ControllerIndexes:findControllers(controllerType)}
437+
newControllerType.Namespace = moduleSource.Namespace()
438+
controllerName := newControllerType.Name()
439+
440+
// Store the first controller only in the controllers map with the unmapped namespace.
441+
if _, found := controllers[controllerName]; !found {
442+
controllers[controllerName] = newControllerType
443+
newControllerType.ModuleSource.AddController(newControllerType)
444+
if newControllerType.ModuleSource == appModule {
445+
// Add the controller mapping into the global namespace
446+
controllers[newControllerType.ShortName()] = newControllerType
447+
}
448+
} else {
449+
ERROR.Printf("Error, attempt to register duplicate controller as %s",controllerName)
450+
}
451+
TRACE.Printf("Registered controller: %s", controllerName)
452+
453+
return
454+
}
384455
// Method searches for a given exported method (case insensitive)
385456
func (ct *ControllerType) Method(name string) *MethodType {
386457
lowerName := strings.ToLower(name)
@@ -392,14 +463,21 @@ func (ct *ControllerType) Method(name string) *MethodType {
392463
return nil
393464
}
394465

395-
var controllers = make(map[string]*ControllerType)
466+
// The controller name with the namespace
467+
func (ct *ControllerType) Name() (string) {
468+
return ct.Namespace + ct.ShortName()
469+
}
470+
471+
// The controller name without the namespace
472+
func (ct *ControllerType) ShortName() (string) {
473+
return strings.ToLower(ct.Type.Name())
474+
}
396475

397476
// RegisterController registers a Controller and its Methods with Revel.
398477
func RegisterController(c interface{}, methods []*MethodType) {
399478
// De-star the controller type
400479
// (e.g. given TypeOf((*Application)(nil)), want TypeOf(Application))
401-
t := reflect.TypeOf(c)
402-
elem := t.Elem()
480+
elem := reflect.TypeOf(c).Elem()
403481

404482
// De-star all of the method arg types too.
405483
for _, m := range methods {
@@ -409,10 +487,10 @@ func RegisterController(c interface{}, methods []*MethodType) {
409487
}
410488
}
411489

412-
controllers[strings.ToLower(elem.Name())] = &ControllerType{
413-
Type: elem,
414-
Methods: methods,
415-
ControllerIndexes: findControllers(elem),
416-
}
417-
TRACE.Printf("Registered controller: %s", elem.Name())
490+
// Fetch module for controller, if none found controller must be part of the app
491+
controllerModule := ModuleFromPath(elem.PkgPath(), true)
492+
493+
controllerType := AddControllerType(controllerModule,elem,methods)
494+
495+
TRACE.Printf("Registered controller: %s", controllerType.Name())
418496
}

fakeapp_test.go

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,18 @@ type Static struct {
2828
*Controller
2929
}
3030

31+
type Implicit struct {
32+
*Controller
33+
}
34+
35+
type Application struct {
36+
*Controller
37+
}
38+
3139
func (c Hotels) Show(id int) Result {
3240
title := "View Hotel"
3341
hotel := &Hotel{id, "A Hotel", "300 Main St.", "New York", "NY", "10010", "USA", 300}
42+
// The line number below must match the one with the code : RenderArgNames: map[int][]string{43: {"title", "hotel"}},
3443
return c.Render(title, hotel)
3544
}
3645

@@ -61,20 +70,10 @@ func (c Static) Serve(prefix, path string) Result {
6170
return c.RenderFile(file, "")
6271
}
6372

64-
func startFakeBookingApp() {
65-
Init("prod", "github.com/revel/revel/testdata", "")
66-
67-
// Disable logging.
68-
TRACE = log.New(ioutil.Discard, "", 0)
69-
INFO = TRACE
70-
WARN = TRACE
71-
ERROR = TRACE
72-
73-
MainTemplateLoader = NewTemplateLoader([]string{ViewsPath, filepath.Join(RevelPath, "templates")})
74-
if err := MainTemplateLoader.Refresh(); err != nil {
75-
ERROR.Fatal(err)
76-
}
77-
73+
// Register controllers is in its own function so the route test can use it as well
74+
func registerControllers() {
75+
controllers = make(map[string]*ControllerType)
76+
fireEvent(ROUTE_REFRESH_REQUESTED, nil)
7877
RegisterController((*Hotels)(nil),
7978
[]*MethodType{
8079
{
@@ -85,7 +84,7 @@ func startFakeBookingApp() {
8584
Args: []*MethodArg{
8685
{"id", reflect.TypeOf((*int)(nil))},
8786
},
88-
RenderArgNames: map[int][]string{34: {"title", "hotel"}},
87+
RenderArgNames: map[int][]string{43: {"title", "hotel"}},
8988
},
9089
{
9190
Name: "Book",
@@ -106,6 +105,52 @@ func startFakeBookingApp() {
106105
RenderArgNames: map[int][]string{},
107106
},
108107
})
108+
RegisterController((*Implicit)(nil),
109+
[]*MethodType{
110+
{
111+
Name: "Implicit",
112+
Args: []*MethodArg{
113+
{Name: "prefix", Type: reflect.TypeOf((*string)(nil))},
114+
{Name: "filepath", Type: reflect.TypeOf((*string)(nil))},
115+
},
116+
RenderArgNames: map[int][]string{},
117+
},
118+
})
119+
RegisterController((*Application)(nil),
120+
[]*MethodType{
121+
{
122+
Name: "Application",
123+
Args: []*MethodArg{
124+
{Name: "prefix", Type: reflect.TypeOf((*string)(nil))},
125+
{Name: "filepath", Type: reflect.TypeOf((*string)(nil))},
126+
},
127+
RenderArgNames: map[int][]string{},
128+
},
129+
{
130+
Name: "Index",
131+
Args: []*MethodArg{
132+
{Name: "foo", Type: reflect.TypeOf((*string)(nil))},
133+
{Name: "bar", Type: reflect.TypeOf((*string)(nil))},
134+
},
135+
RenderArgNames: map[int][]string{},
136+
},
137+
})
138+
}
139+
func startFakeBookingApp() {
140+
Init("prod", "github.com/revel/revel/testdata", "")
141+
142+
// Disable logging.
143+
TRACE = log.New(ioutil.Discard, "", 0)
144+
INFO = TRACE
145+
WARN = TRACE
146+
ERROR = TRACE
147+
148+
MainTemplateLoader = NewTemplateLoader([]string{ViewsPath, filepath.Join(RevelPath, "templates")})
149+
if err := MainTemplateLoader.Refresh(); err != nil {
150+
ERROR.Fatal(err)
151+
}
152+
153+
registerControllers()
109154

110155
runStartupHooks()
111156
}

0 commit comments

Comments
 (0)