5 "github.com/revel/revel/logger"
13 // Module specific functions
15 Name, ImportPath, Path string
16 ControllerTypeList []*ControllerType
17 Log logger.MultiLogger
18 initializedModules map[string]ModuleCallbackInterface
21 // Modules can be called back after they are loaded in revel by using this interface.
22 type ModuleCallbackInterface func(*Module)
24 // The namespace separator constant
25 const namespaceSeperator = `\` // (note cannot be . or : as this is already used for routes)
28 Modules []*Module // The list of modules in use
29 anyModule = &Module{} // Wildcard search for controllers for a module (for backward compatible lookups)
30 appModule = &Module{Name: "App", initializedModules: map[string]ModuleCallbackInterface{}, Log: AppLog} // The app module
31 moduleLog = RevelLog.New("section", "module")
34 // Called by a module init() function, caller will receive the *Module object created for that module
35 // This would be useful for assigning a logger for logging information in the module (since the module context would be correct)
36 func RegisterModuleInit(callback ModuleCallbackInterface) {
37 // Store the module that called this so we can do a callback when the app is initialized
38 // The format %+k is from go-stack/Call.Format and returns the package path
39 key := fmt.Sprintf("%+k", stack.Caller(1))
40 appModule.initializedModules[key] = callback
42 RevelLog.Error("Application already initialized, initializing using app module", "key", key)
47 // Called on startup to make a callback so that modules can be initialized through the `RegisterModuleInit` function
49 AddInitEventHandler(func(typeOf Event, value interface{}) (responseOf EventResponse) {
50 if typeOf == REVEL_BEFORE_MODULES_LOADED {
51 Modules = []*Module{appModule}
52 appModule.Path = filepath.ToSlash(AppPath)
53 appModule.ImportPath = filepath.ToSlash(AppPath)
60 // Returns the namespace for the module in the format `module_name|`
61 func (m *Module) Namespace() (namespace string) {
62 namespace = m.Name + namespaceSeperator
66 // Returns the named controller and action that is in this module
67 func (m *Module) ControllerByName(name, action string) (ctype *ControllerType) {
69 if strings.Index(name, namespaceSeperator) < 0 {
70 comparison = m.Namespace() + name
72 for _, c := range m.ControllerTypeList {
73 if c.Name() == comparison {
81 // Adds the controller type to this module
82 func (m *Module) AddController(ct *ControllerType) {
83 m.ControllerTypeList = append(m.ControllerTypeList, ct)
86 // Based on the full path given return the relevant module
87 // Only to be used on initialization
88 func ModuleFromPath(path string, addGopathToPath bool) (module *Module) {
89 path = filepath.ToSlash(path)
90 gopathList := filepath.SplitList(build.Default.GOPATH)
91 // Strip away the vendor folder
92 if i := strings.Index(path, "/vendor/"); i > 0 {
93 path = path[i+len("vendor/"):]
96 // See if the path exists in the module based
97 for i := range Modules {
99 for _, gopath := range gopathList {
100 if strings.Contains(filepath.ToSlash(filepath.Clean(filepath.Join(gopath, "src", path))), Modules[i].Path) {
106 if strings.Contains(path, Modules[i].ImportPath) {
117 // Default to the app module if not found
124 // ModuleByName returns the module of the given name, if loaded, case insensitive.
125 func ModuleByName(name string) (*Module, bool) {
126 // If the name ends with the namespace separator remove it
127 if name[len(name)-1] == []byte(namespaceSeperator)[0] {
128 name = name[:len(name)-1]
130 name = strings.ToLower(name)
131 if name == strings.ToLower(appModule.Name) {
132 return appModule, true
134 for _, module := range Modules {
135 if strings.ToLower(module.Name) == name {
142 // Loads the modules specified in the config
145 for _, key := range Config.Options("module.") {
146 keys = append(keys, key)
149 // Reorder module order by key name, a poor mans sort but at least it is consistent
151 for _, key := range keys {
152 moduleLog.Debug("Sorted keys", "keys", key)
155 for _, key := range keys {
156 moduleImportPath := Config.StringDefault(key, "")
157 if moduleImportPath == "" {
161 modulePath, err := ResolveImportPath(moduleImportPath)
163 moduleLog.Error("Failed to load module. Import of path failed", "modulePath", moduleImportPath, "error", err)
165 // Drop anything between module.???.<name of module>
166 subKey := key[len("module."):]
167 if index := strings.Index(subKey, "."); index > -1 {
168 subKey = subKey[index+1:]
170 addModule(subKey, moduleImportPath, modulePath)
173 // Modules loaded, now show module path
174 for key, callback := range appModule.initializedModules {
175 if m := ModuleFromPath(key, false); m != nil {
178 RevelLog.Error("Callback for non registered module initializing with application module", "modulePath", key)
184 // called by `loadModules`, creates a new `Module` instance and appends it to the `Modules` list
185 func addModule(name, importPath, modulePath string) {
186 if _, found := ModuleByName(name); found {
187 moduleLog.Panic("Attempt to import duplicate module %s path %s aborting startup", "name", name, "path", modulePath)
189 Modules = append(Modules, &Module{Name: name,
190 ImportPath: filepath.ToSlash(importPath),
191 Path: filepath.ToSlash(modulePath),
192 Log: RootLog.New("module", name)})
193 if codePath := filepath.Join(modulePath, "app"); DirExists(codePath) {
194 CodePaths = append(CodePaths, codePath)
195 if viewsPath := filepath.Join(modulePath, "app", "views"); DirExists(viewsPath) {
196 TemplatePaths = append(TemplatePaths, viewsPath)
200 moduleLog.Debug("Loaded module ", "module", filepath.Base(modulePath))
202 // Hack: There is presently no way for the testrunner module to add the
203 // "test" subdirectory to the CodePaths. So this does it instead.
204 if importPath == Config.StringDefault("module.testrunner", "github.com/revel/modules/testrunner") {
205 joinedPath := filepath.Join(BasePath, "tests")
206 moduleLog.Debug("Found testrunner module, adding `tests` path ", "path", joinedPath)
207 CodePaths = append(CodePaths, joinedPath)
209 if testsPath := filepath.Join(modulePath, "tests"); DirExists(testsPath) {
210 moduleLog.Debug("Found tests path ", "path", testsPath)
211 CodePaths = append(CodePaths, testsPath)