Skip to content

Commit 620d98e

Browse files
committed
Added namespace identity in modules, routes, and templates
1 parent 150849c commit 620d98e

File tree

10 files changed

+619
-240
lines changed

10 files changed

+619
-240
lines changed

controller.go

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package revel
77
import (
88
"errors"
99
"fmt"
10-
"go/build"
1110
"io"
1211
"net/http"
1312
"os"
@@ -92,10 +91,19 @@ func (c *Controller) setStatusIfNil(status int) {
9291
//
9392
// This action will render views/Users/ShowUser.html, passing in an extra
9493
// key-value "user": (User).
94+
//
95+
// This is the slower magical version which uses the runtime
96+
// to determine
97+
// 1) Set c.ViewArgs to the arguments passed into this function
98+
// 2) How to call the RenderTemplate by building the following line
99+
// c.RenderTemplate(c.Name + "/" + c.MethodType.Name + "." + c.Request.Format)
100+
//
101+
// If you want your code to run faster it is recommended you add the template values directly
102+
// to the c.ViewArgs and call c.RenderTemplate directly
95103
func (c *Controller) Render(extraViewArgs ...interface{}) Result {
96104
c.setStatusIfNil(http.StatusOK)
97105

98-
// Get the calling function name.
106+
// Get the calling function line number.
99107
_, _, line, ok := runtime.Caller(1)
100108
if !ok {
101109
ERROR.Println("Failed to get Caller information")
@@ -278,10 +286,11 @@ func (c *Controller) Message(message string, args ...interface{}) (value string)
278286
func (c *Controller) SetAction(controllerName, methodName string) error {
279287

280288
// Look up the controller and method types.
281-
var ok bool
282-
if c.Type, ok = controllers[strings.ToLower(controllerName)]; !ok {
289+
if c.Type = ControllerTypeByName(controllerName, anyModule); c.Type==nil {
283290
return errors.New("revel/controller: failed to find controller " + controllerName)
284291
}
292+
293+
// Note method name is case insensitive search
285294
if c.MethodType = c.Type.Method(methodName); c.MethodType == nil {
286295
return errors.New("revel/controller: failed to find action " + methodName)
287296
}
@@ -294,6 +303,26 @@ func (c *Controller) SetAction(controllerName, methodName string) error {
294303

295304
return nil
296305
}
306+
func ControllerTypeByName(controllerName string, moduleSource *Module) (c *ControllerType) {
307+
var found bool
308+
if c, found = controllers[controllerName]; !found {
309+
// Backup, passed in controllerName should be in lower case, but may not be
310+
if c, found = controllers[strings.ToLower(controllerName)]; !found {
311+
INFO.Printf("Cannot find controller name '%s' in controllers map ", controllerName)
312+
// Search for the controller by name
313+
for _, cType := range controllers {
314+
testControllerName := strings.ToLower(cType.Type.Name())
315+
if testControllerName == strings.ToLower(controllerName) && (cType.ModuleSource == moduleSource || moduleSource == anyModule) {
316+
WARN.Printf("Matched empty namespace controller for %s to this %s", controllerName, cType.ModuleSource.Name)
317+
c = cType
318+
found = true
319+
break
320+
}
321+
}
322+
}
323+
}
324+
return
325+
}
297326

298327
// This is a helper that initializes (zeros) a new app controller value.
299328
// Specifically, it sets all *revel.Controller embedded types to the provided controller.
@@ -365,6 +394,7 @@ func findControllers(appControllerType reflect.Type) (indexes [][]int) {
365394
// Controller registry and types.
366395

367396
type ControllerType struct {
397+
Namespace string // The namespace of the controller
368398
ModuleSource *Module // The module for the controller
369399
Type reflect.Type
370400
Methods []*MethodType
@@ -383,6 +413,30 @@ type MethodArg struct {
383413
Type reflect.Type
384414
}
385415

416+
func AddControllerType(moduleSource *Module,controllerType reflect.Type,methods []*MethodType) (newControllerType *ControllerType) {
417+
if moduleSource==nil {
418+
moduleSource = appModule
419+
}
420+
421+
newControllerType = &ControllerType{ModuleSource:moduleSource,Type:controllerType,Methods:methods,ControllerIndexes:findControllers(controllerType)}
422+
newControllerType.Namespace = moduleSource.Namespace()
423+
controllerName := newControllerType.Name()
424+
425+
// Store the first controller only in the controllers map with the unmapped namespace.
426+
if _, found := controllers[controllerName]; !found {
427+
controllers[controllerName] = newControllerType
428+
newControllerType.ModuleSource.AddController(newControllerType)
429+
if newControllerType.ModuleSource == appModule {
430+
// Add the controller mapping into the global namespace
431+
controllers[newControllerType.ShortName()] = newControllerType
432+
}
433+
} else {
434+
ERROR.Printf("Error, attempt to register duplicate controller as %s",controllerName)
435+
}
436+
TRACE.Printf("Registered controller: %s", controllerName)
437+
438+
return
439+
}
386440
// Method searches for a given exported method (case insensitive)
387441
func (ct *ControllerType) Method(name string) *MethodType {
388442
lowerName := strings.ToLower(name)
@@ -394,6 +448,16 @@ func (ct *ControllerType) Method(name string) *MethodType {
394448
return nil
395449
}
396450

451+
// The controller name without the namespace
452+
func (ct *ControllerType) Name() (string) {
453+
return ct.Namespace + ct.ShortName()
454+
}
455+
456+
// The controller name with the namespace
457+
func (ct *ControllerType) ShortName() (string) {
458+
return strings.ToLower(ct.Type.Name())
459+
}
460+
397461
var controllers = make(map[string]*ControllerType)
398462

399463
// RegisterController registers a Controller and its Methods with Revel.
@@ -409,30 +473,11 @@ func RegisterController(c interface{}, methods []*MethodType) {
409473
arg.Type = arg.Type.Elem()
410474
}
411475
}
412-
path := elem.PkgPath()
413-
gopathList := filepath.SplitList(build.Default.GOPATH)
414-
var controllerModule *Module
415-
416-
// See if the path exists in the module based
417-
for i := range Modules {
418-
found := false
419-
for _, gopath := range gopathList {
420-
if strings.HasPrefix(gopath+"/src/"+path, Modules[i].Path) {
421-
controllerModule = Modules[i]
422-
found = true
423-
break
424-
}
425-
}
426-
if found {
427-
break
428-
}
429-
}
430476

431-
controllers[strings.ToLower(elem.Name())] = &ControllerType{
432-
ModuleSource: controllerModule,
433-
Type: elem,
434-
Methods: methods,
435-
ControllerIndexes: findControllers(elem),
436-
}
437-
TRACE.Printf("Registered controller: %s", elem.Name())
477+
// Fetch module for controller, if none found controller must be part of the app
478+
controllerModule := ModuleFromPath(elem.PkgPath(), true)
479+
480+
controllerType := AddControllerType(controllerModule,elem,methods)
481+
482+
TRACE.Printf("Registered controller: %s", controllerType.Name())
438483
}

fakeapp_test.go

Lines changed: 52 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,44 @@ 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+
}
131+
func startFakeBookingApp() {
132+
Init("prod", "github.com/revel/revel/testdata", "")
133+
134+
// Disable logging.
135+
TRACE = log.New(ioutil.Discard, "", 0)
136+
INFO = TRACE
137+
WARN = TRACE
138+
ERROR = TRACE
139+
140+
MainTemplateLoader = NewTemplateLoader([]string{ViewsPath, filepath.Join(RevelPath, "templates")})
141+
if err := MainTemplateLoader.Refresh(); err != nil {
142+
ERROR.Fatal(err)
143+
}
144+
145+
registerControllers()
109146

110147
runStartupHooks()
111148
}

module.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package revel
2+
3+
import (
4+
"sort"
5+
"strings"
6+
"path/filepath"
7+
"go/build"
8+
)
9+
10+
// Module specific functions
11+
type Module struct {
12+
Name, ImportPath, Path string
13+
ControllerTypeList []*ControllerType
14+
}
15+
16+
const namespaceSeperator = "|" // ., : are already used
17+
18+
var (
19+
anyModule = &Module{}
20+
appModule = &Module{Name:"App"}
21+
)
22+
23+
// Returns the namespace for the module in the format `module_name|`
24+
func (m *Module) Namespace() (namespace string) {
25+
namespace = m.Name + namespaceSeperator
26+
return
27+
}
28+
29+
// Returns the named controller and action that is in this module
30+
func (m *Module) ControllerByName(name,action string)(ctype *ControllerType) {
31+
comparision := name
32+
if strings.Index(name,namespaceSeperator)<0 {
33+
comparision = m.Namespace() + name
34+
}
35+
for _,c := range m.ControllerTypeList {
36+
if strings.Index(c.Name(),comparision)>-1 {
37+
ctype = c
38+
break
39+
}
40+
}
41+
return
42+
}
43+
func (m *Module) AddController(ct *ControllerType) {
44+
m.ControllerTypeList = append(m.ControllerTypeList,ct)
45+
}
46+
47+
48+
func loadModules() {
49+
keys := []string{}
50+
for _, key := range Config.Options("module.") {
51+
keys = append(keys, key)
52+
}
53+
54+
// Reorder module order by key name, a poor mans sort but at least it is consistent
55+
sort.Strings(keys)
56+
for _, key := range keys {
57+
INFO.Println("Sorted keys", key)
58+
59+
}
60+
for _, key := range keys {
61+
moduleImportPath := Config.StringDefault(key, "")
62+
if moduleImportPath == "" {
63+
continue
64+
}
65+
66+
modulePath, err := ResolveImportPath(moduleImportPath)
67+
if err != nil {
68+
ERROR.Fatalln("Failed to load module. Import of", moduleImportPath, "failed:", err)
69+
}
70+
// Drop anything between module.???.<name of module>
71+
subKey := key[len("module."):]
72+
if index := strings.Index(subKey, "."); index > -1 {
73+
subKey = subKey[index+1:]
74+
}
75+
76+
addModule(subKey, moduleImportPath, modulePath)
77+
}
78+
}
79+
80+
// Based on the full path given return the relevant module
81+
// Only be used on initialization
82+
func ModuleFromPath(path string, addGopathToPath bool) (module *Module) {
83+
gopathList := filepath.SplitList(build.Default.GOPATH)
84+
85+
// See if the path exists in the module based
86+
for i := range Modules {
87+
if addGopathToPath {
88+
for _, gopath := range gopathList {
89+
if strings.HasPrefix(gopath+"/src/"+path, Modules[i].Path) {
90+
module = Modules[i]
91+
break
92+
}
93+
}
94+
} else {
95+
if strings.HasPrefix(path, Modules[i].Path) {
96+
module = Modules[i]
97+
break
98+
}
99+
100+
}
101+
102+
if module!=nil {
103+
break
104+
}
105+
}
106+
return
107+
}
108+

0 commit comments

Comments
 (0)