Enhancements to Revel command

Reformat of code
Allow user to use a mix of command line arguments and flags
Enhance the import tool to detect missing packages in the modules side
Added test cases for all commands
This commit is contained in:
NotZippy
2018-09-27 21:08:40 -07:00
parent 01ccd695d4
commit f4fb2ec091
65 changed files with 2014 additions and 1281 deletions

View File

@@ -6,92 +6,96 @@ import (
"github.com/revel/config"
"go/build"
"os"
"errors"
"fmt"
"path/filepath"
"sort"
"strings"
)
const (
// Event type when templates are going to be refreshed (receivers are registered template engines added to the template.engine conf option)
TEMPLATE_REFRESH_REQUESTED = iota
// Event type when templates are refreshed (receivers are registered template engines added to the template.engine conf option)
TEMPLATE_REFRESH_COMPLETED
// Event type before all module loads, events thrown to handlers added to AddInitEventHandler
// Event type before all module loads, events thrown to handlers added to AddInitEventHandler
REVEL_BEFORE_MODULES_LOADED
// Event type called when a new module is found
REVEL_BEFORE_MODULE_LOADED
// Event type called when after a new module is found
REVEL_AFTER_MODULE_LOADED
// Event type after all module loads, events thrown to handlers added to AddInitEventHandler
REVEL_AFTER_MODULES_LOADED
// Event type before server engine is initialized, receivers are active server engine and handlers added to AddInitEventHandler
ENGINE_BEFORE_INITIALIZED
// Event type before server engine is started, receivers are active server engine and handlers added to AddInitEventHandler
ENGINE_STARTED
// Event type after server engine is stopped, receivers are active server engine and handlers added to AddInitEventHandler
ENGINE_SHUTDOWN
// Called before routes are refreshed
ROUTE_REFRESH_REQUESTED
// Called after routes have been refreshed
ROUTE_REFRESH_COMPLETED
)
type (
// The container object for describing all Revels variables
RevelContainer struct {
ImportPath string // The import path
SourcePath string // The full source path
RunMode string // The current run mode
RevelPath string // The path to the Revel source code
BasePath string // The base path to the application
AppPath string // The application path (BasePath + "/app"
ViewsPath string // The application views path
CodePaths []string // All the code paths
TemplatePaths []string // All the template paths
ConfPaths []string // All the configuration paths
Config *config.Context // The global config object
Packaged bool // True if packaged
DevMode bool // True if running in dev mode
HTTPPort int // The http port
HTTPAddr string // The http address
HTTPSsl bool // True if running https
HTTPSslCert string // The SSL certificate
HTTPSslKey string // The SSL key
AppName string // The application name
AppRoot string // The application root from the config `app.root`
CookiePrefix string // The cookie prefix
CookieDomain string // The cookie domain
CookieSecure bool // True if cookie is secure
SecretStr string // The secret string
MimeConfig *config.Context // The mime configuration
BuildPaths struct {
Revel string
}
Paths struct {
Import string
Source string
Base string
App string
Views string
Code []string
Template []string
Config []string
}
PackageInfo struct {
Config config.Context
Packaged bool
DevMode bool
Vendor bool
}
Application struct {
Name string
Root string
}
ImportPath string // The import path
SourcePath string // The full source path
RunMode string // The current run mode
RevelPath string // The path to the Revel source code
BasePath string // The base path to the application
AppPath string // The application path (BasePath + "/app")
ViewsPath string // The application views path
CodePaths []string // All the code paths
TemplatePaths []string // All the template paths
ConfPaths []string // All the configuration paths
Config *config.Context // The global config object
Packaged bool // True if packaged
DevMode bool // True if running in dev mode
HTTPPort int // The http port
HTTPAddr string // The http address
HTTPSsl bool // True if running https
HTTPSslCert string // The SSL certificate
HTTPSslKey string // The SSL key
AppName string // The application name
AppRoot string // The application root from the config `app.root`
CookiePrefix string // The cookie prefix
CookieDomain string // The cookie domain
CookieSecure bool // True if cookie is secure
SecretStr string // The secret string
MimeConfig *config.Context // The mime configuration
ModulePathMap map[string]string // The module path map
}
RevelCallback interface {
FireEvent(key int, value interface{}) (response int)
WrappedRevelCallback struct {
FireEventFunction func(key Event, value interface{}) (response EventResponse)
ImportFunction func(pkgName string) error
}
doNothingRevelCallback struct {
}
)
// Simple callback to pass to the RevelCallback that does nothing
var DoNothingRevelCallback = RevelCallback(&doNothingRevelCallback{})
// Simple Wrapped RevelCallback
func NewWrappedRevelCallback(fe func(key Event, value interface{}) (response EventResponse), ie func(pkgName string) error) RevelCallback {
return &WrappedRevelCallback{fe, ie}
}
func (_ *doNothingRevelCallback) FireEvent(key int, value interface{}) (response int) {
// Function to implement the FireEvent
func (w *WrappedRevelCallback) FireEvent(key Event, value interface{}) (response EventResponse) {
if w.FireEventFunction != nil {
response = w.FireEventFunction(key, value)
}
return
}
func (w *WrappedRevelCallback) PackageResolver(pkgName string) error {
return w.ImportFunction(pkgName)
}
// RevelImportPath Revel framework import path
var RevelImportPath = "github.com/revel/revel"
// This function returns a container object describing the revel application
// eventually this type of function will replace the global variables.
func NewRevelPaths(mode, importPath, srcPath string, callback RevelCallback) (rp *RevelContainer) {
func NewRevelPaths(mode, importPath, srcPath string, callback RevelCallback) (rp *RevelContainer, err error) {
rp = &RevelContainer{ModulePathMap: map[string]string{}}
// Ignore trailing slashes.
rp.ImportPath = strings.TrimRight(importPath, "/")
@@ -101,17 +105,20 @@ func NewRevelPaths(mode, importPath, srcPath string, callback RevelCallback) (rp
// If the SourcePath is not specified, find it using build.Import.
var revelSourcePath string // may be different from the app source path
if rp.SourcePath == "" {
revelSourcePath, rp.SourcePath = findSrcPaths(importPath)
rp.SourcePath, revelSourcePath, err = utils.FindSrcPaths(importPath, RevelImportPath, callback.PackageResolver)
if err != nil {
return
}
} else {
// If the SourcePath was specified, assume both Revel and the app are within it.
rp.SourcePath = filepath.Clean(rp.SourcePath)
revelSourcePath = rp.SourcePath
}
// Setup paths for application
rp.RevelPath = filepath.Join(revelSourcePath, filepath.FromSlash(RevelImportPath))
rp.BasePath = filepath.Join(rp.SourcePath, filepath.FromSlash(importPath))
rp.PackageInfo.Vendor = utils.Exists(filepath.Join(rp.BasePath, "vendor"))
rp.AppPath = filepath.Join(rp.BasePath, "app")
rp.ViewsPath = filepath.Join(rp.AppPath, "views")
@@ -133,11 +140,9 @@ func NewRevelPaths(mode, importPath, srcPath string, callback RevelCallback) (rp
},
rp.ConfPaths...)
var err error
rp.Config, err = config.LoadContext("app.conf", rp.ConfPaths)
if err != nil {
utils.Logger.Fatal("Unable to load configuartion file ","error", err)
os.Exit(1)
return rp, fmt.Errorf("Unable to load configuartion file %s", err)
}
// Ensure that the selected runmode appears in app.conf.
@@ -146,7 +151,7 @@ func NewRevelPaths(mode, importPath, srcPath string, callback RevelCallback) (rp
mode = config.DefaultSection
}
if !rp.Config.HasSection(mode) {
utils.Logger.Fatal("app.conf: No mode found:","run-mode", mode)
return rp, fmt.Errorf("app.conf: No mode found: %s %s", "run-mode", mode)
}
rp.Config.SetSection(mode)
@@ -159,10 +164,10 @@ func NewRevelPaths(mode, importPath, srcPath string, callback RevelCallback) (rp
rp.HTTPSslKey = rp.Config.StringDefault("http.sslkey", "")
if rp.HTTPSsl {
if rp.HTTPSslCert == "" {
utils.Logger.Fatal("No http.sslcert provided.")
return rp, errors.New("No http.sslcert provided.")
}
if rp.HTTPSslKey == "" {
utils.Logger.Fatal("No http.sslkey provided.")
return rp, errors.New("No http.sslkey provided.")
}
}
//
@@ -173,21 +178,23 @@ func NewRevelPaths(mode, importPath, srcPath string, callback RevelCallback) (rp
rp.CookieSecure = rp.Config.BoolDefault("cookie.secure", rp.HTTPSsl)
rp.SecretStr = rp.Config.StringDefault("app.secret", "")
callback.FireEvent(REVEL_BEFORE_MODULES_LOADED, nil)
rp.loadModules(callback)
if err := rp.loadModules(callback); err != nil {
return rp, err
}
callback.FireEvent(REVEL_AFTER_MODULES_LOADED, nil)
return
}
// LoadMimeConfig load mime-types.conf on init.
func (rp *RevelContainer) LoadMimeConfig() {
var err error
func (rp *RevelContainer) LoadMimeConfig() (err error) {
rp.MimeConfig, err = config.LoadContext("mime-types.conf", rp.ConfPaths)
if err != nil {
utils.Logger.Fatal("Failed to load mime type config:", "error", err)
return fmt.Errorf("Failed to load mime type config: %s %s", "error", err)
}
return
}
// Loads modules based on the configuration setup.
@@ -195,7 +202,7 @@ func (rp *RevelContainer) LoadMimeConfig() {
// for each module loaded. The callback will receive the RevelContainer, name, moduleImportPath and modulePath
// It will automatically add in the code paths for the module to the
// container object
func (rp *RevelContainer) loadModules(callback RevelCallback) {
func (rp *RevelContainer) loadModules(callback RevelCallback) (err error) {
keys := []string{}
for _, key := range rp.Config.Options("module.") {
keys = append(keys, key)
@@ -211,7 +218,12 @@ func (rp *RevelContainer) loadModules(callback RevelCallback) {
modulePath, err := rp.ResolveImportPath(moduleImportPath)
if err != nil {
utils.Logger.Error("Failed to load module. Import of path failed", "modulePath", moduleImportPath, "error", err)
utils.Logger.Info("Missing module ", "module", moduleImportPath, "error",err)
callback.PackageResolver(moduleImportPath)
modulePath, err = rp.ResolveImportPath(moduleImportPath)
if err != nil {
return fmt.Errorf("Failed to load module. Import of path failed %s:%s %s:%s ", "modulePath", moduleImportPath, "error", err)
}
}
// Drop anything between module.???.<name of module>
name := key[len("module."):]
@@ -222,6 +234,7 @@ func (rp *RevelContainer) loadModules(callback RevelCallback) {
rp.addModulePaths(name, moduleImportPath, modulePath)
callback.FireEvent(REVEL_AFTER_MODULE_LOADED, []interface{}{rp, name, moduleImportPath, modulePath})
}
return
}
// Adds a module paths to the container object
@@ -252,43 +265,12 @@ func (rp *RevelContainer) ResolveImportPath(importPath string) (string, error) {
return filepath.Join(rp.SourcePath, importPath), nil
}
modPkg, err := build.Import(importPath, rp.RevelPath, build.FindOnly)
modPkg, err := build.Import(importPath, rp.AppPath, build.FindOnly)
if err != nil {
return "", err
}
if rp.PackageInfo.Vendor && !strings.HasPrefix(modPkg.Dir,rp.BasePath) {
return "", fmt.Errorf("Module %s was found outside of path %s.",importPath, modPkg.Dir)
}
return modPkg.Dir, nil
}
// Find the full source dir for the import path, uses the build.Default.GOPATH to search for the directory
func findSrcPaths(importPath string) (revelSourcePath, appSourcePath string) {
var (
gopaths = filepath.SplitList(build.Default.GOPATH)
goroot = build.Default.GOROOT
)
if len(gopaths) == 0 {
utils.Logger.Fatalf("GOPATH environment variable is not set. " +
"Please refer to http://golang.org/doc/code.html to configure your Go environment.")
}
if utils.ContainsString(gopaths, goroot) {
utils.Logger.Fatalf("GOPATH (%s) must not include your GOROOT (%s). "+
"Please refer to http://golang.org/doc/code.html to configure your Go environment.",
gopaths, goroot)
}
appPkg, err := build.Import(importPath, "", build.FindOnly)
if err != nil {
utils.Logger.Fatal("Failed to import "+importPath+" with error:", "error", err)
}
revelPkg, err := build.Import(RevelImportPath, appPkg.Dir, build.FindOnly)
if err != nil {
utils.Logger.Fatal("Failed to find Revel with error:", "error", err)
}
revelSourcePath, appSourcePath = revelPkg.Dir[:len(revelPkg.Dir)-len(RevelImportPath)], appPkg.SrcRoot
return
}