// Load a single message file func loadMessageFile(path string, info os.FileInfo, osError error) error { if osError != nil { return osError } if info.IsDir() { return nil } if matched, _ := regexp.MatchString(messageFilePattern, info.Name()); matched { if config, error := parseMessagesFile(path); error != nil { return error } else { locale := parseLocaleFromFileName(info.Name()) // If we have already parsed a message file for this locale, merge both if _, exists := messages[locale]; exists { messages[locale].Merge(config) glog.V(1).Infof("Successfully merged messages for locale '%s'", locale) } else { messages[locale] = config } glog.V(1).Infoln("Successfully loaded messages from file", info.Name()) } } else { glog.V(1).Infof("Ignoring file %s because it did not have a valid extension", info.Name()) } return nil }
func I18nFilter(c *Controller, fc []Filter) { if foundCookie, cookieValue := hasLocaleCookie(c.Request); foundCookie { glog.V(1).Infof("Found locale cookie value: %s", cookieValue) setCurrentLocaleControllerArguments(c, cookieValue) } else if foundHeader, headerValue := hasAcceptLanguageHeader(c.Request); foundHeader { glog.V(1).Infof("Found Accept-Language header value: %s", headerValue) setCurrentLocaleControllerArguments(c, headerValue) } else { glog.V(1).Info("Unable to find locale in cookie or header, using empty string") setCurrentLocaleControllerArguments(c, "") } fc[0](c, fc[1:]) }
func ActionInvoker(c *Controller, _ []Filter) { // Instantiate the method. methodValue := reflect.ValueOf(c.AppController).MethodByName(c.MethodType.Name) // Collect the values for the method's arguments. var methodArgs []reflect.Value for _, arg := range c.MethodType.Args { // If they accept a websocket connection, treat that arg specially. var boundArg reflect.Value if arg.Type == websocketType { boundArg = reflect.ValueOf(c.Websocket) } else { glog.V(1).Infoln("Binding:", arg.Name, "as", arg.Type) boundArg = Bind(c.Params, arg.Name, arg.Type) } methodArgs = append(methodArgs, boundArg) } var resultValue reflect.Value if methodValue.Type().IsVariadic() { resultValue = methodValue.CallSlice(methodArgs)[0] } else { resultValue = methodValue.Call(methodArgs)[0] } if resultValue.Kind() == reflect.Interface && !resultValue.IsNil() { c.Result = resultValue.Interface().(Result) } }
// Perform a message look-up for the given locale and message using the given arguments. // // When either an unknown locale or message is detected, a specially formatted string is returned. func Message(locale, message string, args ...interface{}) string { language, region := parseLocale(locale) glog.V(1).Infof("Resolving message '%s' for language '%s' and region '%s'", message, language, region) var value string var err error messageConfig, knownLanguage := messages[language] if knownLanguage { // This works because unlike the goconfig documentation suggests it will actually // try to resolve message in DEFAULT if it did not find it in the given section. value, err = messageConfig.String(region, message) if err != nil { glog.V(1).Infof("Unknown message '%s' for locale '%s', trying default language", message, locale) // Continue to try default language } } else { glog.V(1).Infof("Unsupported language for locale '%s' and message '%s', trying default language", locale, message) } if value == "" { if defaultLanguage, found := Config.String(defaultLanguageOption); found { glog.V(1).Infof("Using default language '%s'", defaultLanguage) messageConfig, knownLanguage = messages[defaultLanguage] if !knownLanguage { glog.Warningf("Unsupported default language for locale '%s' and message '%s'", defaultLanguage, message) return fmt.Sprintf(unknownValueFormat, message) } value, err = messageConfig.String(region, message) if err != nil { glog.Warningf("Unknown message '%s' for default locale '%s'", message, locale) return fmt.Sprintf(unknownValueFormat, message) } } else { glog.Warningf("Unable to find default language option (%s); messages for unsupported locales will never be translated", defaultLanguageOption) return fmt.Sprintf(unknownValueFormat, message) } } if len(args) > 0 { glog.V(1).Infof("Arguments detected, formatting '%s' with %v", value, args) value = fmt.Sprintf(value, args...) } return value }
// Terminate the app server if it's running. func (cmd AppCmd) Kill() { if cmd.Cmd != nil && (cmd.ProcessState == nil || !cmd.ProcessState.Exited()) { glog.V(1).Infoln("Killing revel server pid", cmd.Process.Pid) err := cmd.Process.Kill() if err != nil { glog.Fatalln("Failed to kill revel server:", err) } } }
// Determine whether the given request has a valid language cookie value. func hasLocaleCookie(request *Request) (bool, string) { if request != nil && request.Cookies() != nil { name := Config.StringDefault(localeCookieConfigKey, CookiePrefix+"_LANG") if cookie, error := request.Cookie(name); error == nil { return true, cookie.Value } else { glog.V(1).Infof("Unable to read locale cookie with name '%s': %s", name, error.Error()) } } return false, "" }
func runApp(args []string) { if len(args) == 0 { errorf("No import path given.\nRun 'revel help run' for usage.\n") } // Determine the run mode. mode := "dev" if len(args) >= 2 { mode = args[1] } // Find and parse app.conf revel.Init(mode, args[0], "") revel.LoadModules() revel.LoadMimeConfig() // Set working directory to BasePath, to make relative paths convenient and // dependable. if err := os.Chdir(revel.BasePath); err != nil { log.Fatalln("Failed to change directory into app path: ", err) } // Determine the override port, if any. port := revel.HttpPort if len(args) == 3 { var err error if port, err = strconv.Atoi(args[2]); err != nil { errorf("Failed to parse port as integer: %s", args[2]) } } glog.Infof("Running %s (%s) in %s mode", revel.AppName, revel.ImportPath, mode) glog.V(1).Info("Base path: ", revel.BasePath) // If the app is run in "watched" mode, use the harness to run it. if revel.Config.BoolDefault("watch", true) && revel.Config.BoolDefault("watch.code", true) { revel.HttpPort = port harness.NewHarness().Run() // Never returns. } // Else, just build and run the app. app, err := harness.Build() if err != nil { errorf("Failed to build app: %s", err) } app.Port = port app.Cmd().Run() }
func addImports(imports map[string]string, decl ast.Decl, srcDir string) { genDecl, ok := decl.(*ast.GenDecl) if !ok { return } if genDecl.Tok != token.IMPORT { return } for _, spec := range genDecl.Specs { importSpec := spec.(*ast.ImportSpec) var pkgAlias string if importSpec.Name != nil { pkgAlias = importSpec.Name.Name if pkgAlias == "_" { continue } } quotedPath := importSpec.Path.Value // e.g. "\"sample/app/models\"" fullPath := quotedPath[1 : len(quotedPath)-1] // Remove the quotes // If the package was not aliased (common case), we have to import it // to see what the package name is. // TODO: Can improve performance here a lot: // 1. Do not import everything over and over again. Keep a cache. // 2. Exempt the standard library; their directories always match the package name. // 3. Can use build.FindOnly and then use parser.ParseDir with mode PackageClauseOnly if pkgAlias == "" { pkg, err := build.Import(fullPath, srcDir, 0) if err != nil { // We expect this to happen for apps using reverse routing (since we // have not yet generated the routes). Don't log that. if !strings.HasSuffix(fullPath, "/app/routes") { glog.V(1).Info("Could not find import: ", fullPath) } continue } pkgAlias = pkg.Name } imports[pkgAlias] = fullPath } }
// Rebuild the Revel application and run it on the given port. func (h *Harness) Refresh() (err *revel.Error) { if h.app != nil { h.app.Kill() } glog.V(1).Info("Rebuild") h.app, err = Build() if err != nil { return } h.app.Port = h.port if err2 := h.app.Cmd().Start(); err2 != nil { return &revel.Error{ Title: "App failed to start up", Description: err2.Error(), } } return }
// Start the app server, and wait until it is ready to serve requests. func (cmd AppCmd) Start() error { listeningWriter := startupListeningWriter{os.Stdout, make(chan bool)} cmd.Stdout = listeningWriter glog.V(1).Infoln("Exec app:", cmd.Path, cmd.Args) if err := cmd.Cmd.Start(); err != nil { glog.Fatalln("Error running:", err) } select { case <-cmd.waitChan(): return errors.New("revel/harness: app died") case <-time.After(30 * time.Second): cmd.Kill() return errors.New("revel/harness: app timed out") case <-listeningWriter.notifyReady: return nil } panic("Impossible") }
// Register a Controller and its Methods with Revel. func RegisterController(c interface{}, methods []*MethodType) { // De-star the controller type // (e.g. given TypeOf((*Application)(nil)), want TypeOf(Application)) var t reflect.Type = reflect.TypeOf(c) var elem reflect.Type = t.Elem() // De-star all of the method arg types too. for _, m := range methods { m.lowerName = strings.ToLower(m.Name) for _, arg := range m.Args { arg.Type = arg.Type.Elem() } } controllers[strings.ToLower(elem.Name())] = &ControllerType{ Type: elem, Methods: methods, ControllerIndexes: findControllers(elem), } glog.V(1).Infof("Registered controller: %s", elem.Name()) }
// getStructTypeDecl checks if the given decl is a type declaration for a // struct. If so, the TypeSpec is returned. func getStructTypeDecl(decl ast.Decl) (spec *ast.TypeSpec, found bool) { genDecl, ok := decl.(*ast.GenDecl) if !ok { return } if genDecl.Tok != token.TYPE { return } if len(genDecl.Specs) != 1 { glog.V(1).Infof("Surprising: Decl does not have 1 Spec: %v", genDecl) return } spec = genDecl.Specs[0].(*ast.TypeSpec) if _, ok := spec.Type.(*ast.StructType); ok { found = true } return }
// Refresh scans the views directory and parses all templates using the // configured TemplateEngines. If a template fails to parse, the error is set // on the loader (and returned). func (loader *TemplateLoader) Refresh() *Error { glog.V(1).Infof("Refreshing templates from %s", loader.paths) loader.compileError = nil loader.templatePaths = map[string]string{} // Set the template delimiters for the project if present, then split into left // and right delimiters around a space character var splitDelims []string if delims := Config.StringDefault("template.delimiters", ""); delims != "" { splitDelims = strings.Split(delims, " ") if len(splitDelims) != 2 { glog.Fatalln("app.conf: Incorrect format for template.delimiters") } } loader.defaultEngine = NewTextTemplateEngine() loader.engines = map[string]TemplateEngine{ ".html": NewHtmlTemplateEngine(), ".xml": NewHtmlTemplateEngine(), ".json": NewTextTemplateEngine(), ".txt": NewTextTemplateEngine(), } // Walk through the template loader's paths and pass each template to the // appropriate engine. for _, basePath := range loader.paths { // Walk only returns an error if the template loader is completely unusable // (namely, if one of the TemplateFuncs does not have an acceptable signature). funcErr := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) (walkErr error) { defer func() { if err := recover(); err != nil { walkErr = &Error{ Title: "Panic (Template Loader)", Description: fmt.Sprintln(err), } } }() if err != nil { glog.Errorln("error walking templates:", err) return nil } // Walk into directories. if info.IsDir() { if !loader.WatchDir(info.Name()) { return filepath.SkipDir } return nil } if !loader.WatchFile(info.Name()) { return nil } // Convert template names to use forward slashes, even on Windows. templateName := path[len(basePath)+1:] if os.PathSeparator == '\\' { templateName = strings.Replace(templateName, "\\", "/", -1) } // If we already loaded a template of this name, skip it. if _, ok := loader.templatePaths[templateName]; ok { return nil } loader.templatePaths[templateName] = path fileBytes, err := ioutil.ReadFile(path) if err != nil { glog.Errorln("Failed reading file:", path) return nil } ext := filepath.Ext(templateName) engine, ok := loader.engines[ext] if !ok { engine = loader.defaultEngine } // If alternate delimiters set for the project, change them for this template. if splitDelims != nil { if strings.HasPrefix(path, ViewsPath) { engine.Delims(splitDelims[0], splitDelims[1]) } else { engine.Delims("", "") } } err = engine.Parse(templateName, string(fileBytes)) // Store / report the first error encountered. if err != nil && loader.compileError == nil { _, line, description := parseTemplateError(err) loader.compileError = &Error{ Title: "Template Compilation Error", Path: templateName, Description: description, Line: line, SourceLines: strings.Split(string(fileBytes), "\n"), } glog.Errorf("Template compilation error (In %s around line %d):\n%s", templateName, line, description) } return nil }) // If there was an error with the Funcs, set it and return immediately. if funcErr != nil { loader.compileError = funcErr.(*Error) return loader.compileError } } return loader.compileError }
// Run the app server inline. Never returns. func (cmd AppCmd) Run() { glog.V(1).Infoln("Exec app:", cmd.Path, cmd.Args) if err := cmd.Cmd.Run(); err != nil { glog.Fatalln("Error running:", err) } }
// Build the app: // 1. Generate the the main.go file. // 2. Run the appropriate "go build" command. // Requires that revel.Init has been called previously. // Returns the path to the built binary, and an error if there was a problem building it. func Build() (app *App, compileError *revel.Error) { start := time.Now() // First, clear the generated files (to avoid them messing with ProcessSource). cleanSource("tmp", "routes") sourceInfo, compileError := ProcessSource(revel.CodePaths) if compileError != nil { return nil, compileError } // Add the db.import to the import paths. if dbImportPath, found := revel.Config.String("db.import"); found { sourceInfo.InitImportPaths = append(sourceInfo.InitImportPaths, dbImportPath) } // Generate two source files. templateArgs := map[string]interface{}{ "Controllers": sourceInfo.ControllerSpecs(), "ValidationKeys": sourceInfo.ValidationKeys, "ImportPaths": calcImportAliases(sourceInfo), "TestSuites": sourceInfo.TestSuites(), } genSource("tmp", "main.go", MAIN, templateArgs) genSource("routes", "routes.go", ROUTES, templateArgs) // Read build config. buildTags := revel.Config.StringDefault("build.tags", "") // Build the user program (all code under app). // It relies on the user having "go" installed. goPath, err := exec.LookPath("go") if err != nil { glog.Fatalf("Go executable not found in PATH.") } pkg, err := build.Default.Import(revel.ImportPath, "", build.FindOnly) if err != nil { glog.Fatalln("Failure importing", revel.ImportPath) } binName := filepath.Join(pkg.BinDir, filepath.Base(revel.BasePath)) if runtime.GOOS == "windows" { binName += ".exe" } gotten := make(map[string]struct{}) for { buildCmd := exec.Command(goPath, "build", "-i", "-tags", buildTags, "-o", binName, path.Join(revel.ImportPath, "app", "tmp")) glog.V(1).Infoln("Exec:", buildCmd.Args) output, err := buildCmd.CombinedOutput() // If the build succeeded, we're done. if err == nil { glog.Infof("Build took %s", time.Since(start)) return NewApp(binName), nil } glog.Error(string(output)) // See if it was an import error that we can go get. matches := importErrorPattern.FindStringSubmatch(string(output)) if matches == nil { return nil, newCompileError(output) } // Ensure we haven't already tried to go get it. pkgName := matches[1] if _, alreadyTried := gotten[pkgName]; alreadyTried { return nil, newCompileError(output) } gotten[pkgName] = struct{}{} // Execute "go get <pkg>" getCmd := exec.Command(goPath, "get", pkgName) glog.V(1).Infoln("Exec:", getCmd.Args) getOutput, err := getCmd.CombinedOutput() if err != nil { glog.Error(string(getOutput)) return nil, newCompileError(output) } // Success getting the import, attempt to build again. } glog.Fatal("Not reachable") return nil, nil }