// genSource renders the given template to produce source code, which it writes // to the given directory and file. func genSource(dir, filename, templateSource string, args map[string]interface{}) { sourceCode := revel.ExecuteTemplate( template.Must(template.New("").Parse(templateSource)), args) // Create a fresh dir. tmpPath := filepath.Join(revel.AppPath, dir) err := os.RemoveAll(tmpPath) if err != nil { glog.Errorln("Failed to remove dir:", err) } err = os.Mkdir(tmpPath, 0777) if err != nil { glog.Fatalln("Failed to make tmp directory:", err) } // Create the file file, err := os.Create(filepath.Join(tmpPath, filename)) defer file.Close() if err != nil { glog.Fatalln("Failed to create file:", err) } _, err = file.WriteString(sourceCode) if err != nil { glog.Fatalln("Failed to write to file:", err) } }
// Recursively read and cache all available messages from all message files on the given path. func loadMessages(path string) { messages = make(map[string]*config.Config) if error := filepath.Walk(path, loadMessageFile); error != nil && !os.IsNotExist(error) { glog.Errorln("Error reading messages files:", error) } }
// Render a template corresponding to the calling Controller method. // Arguments will be added to c.RenderArgs prior to rendering the template. // They are keyed on their local identifier. // // For example: // // func (c Users) ShowUser(id int) revel.Result { // user := loadUser(id) // return c.Render(user) // } // // This action will render views/Users/ShowUser.html, passing in an extra // key-value "user": (User). // // Content negotiation // // The template selected depends on the request's format (html, json, xml, txt), // (which is derived from the Accepts header). For example, if Request.Format // was "json", then the above example would look for the // views/Users/ShowUser.json template instead. // // If no template is found and the format is one of "json" or "xml", // then Render will instead serialize the first argument into that format. func (c *Controller) Render(extraRenderArgs ...interface{}) Result { templatePath := c.Name + "/" + c.MethodType.Name + "." + c.Request.Format // Get the calling function name. _, _, line, ok := runtime.Caller(1) if !ok { glog.Error("Failed to get Caller information") } // If not HTML, first check if the template is present. template, err := MainTemplateLoader.Template(templatePath) // If not, and there is an arg, serialize that if it's xml or json. if template == nil { if len(extraRenderArgs) > 0 { switch c.Request.Format { case "xml": return c.RenderXml(extraRenderArgs[0]) case "json": return c.RenderJson(extraRenderArgs[0]) } } // Else, render a 404 error saying we couldn't find the template. return c.NotFound(err.Error()) } // Get the extra RenderArgs passed in. if renderArgNames, ok := c.MethodType.RenderArgNames[line]; ok { if len(renderArgNames) == len(extraRenderArgs) { for i, extraRenderArg := range extraRenderArgs { c.RenderArgs[renderArgNames[i]] = extraRenderArg } } else { glog.Errorln(len(renderArgNames), "RenderArg names found for", len(extraRenderArgs), "extra RenderArgs") } } else { glog.Errorln("No RenderArg names found for Render call on line", line, "(Method", c.MethodType.Name, ")") } return &RenderTemplateResult{ Template: template, RenderArgs: c.RenderArgs, } }
func cleanSource(dirs ...string) { for _, dir := range dirs { tmpPath := filepath.Join(revel.AppPath, dir) err := os.RemoveAll(tmpPath) if err != nil { glog.Errorln("Failed to remove dir:", err) } } }
func (r *RedirectToActionResult) Apply(req *Request, resp *Response) { url, err := getRedirectUrl(r.val) if err != nil { glog.Errorln("Couldn't resolve redirect:", err.Error()) ErrorResult{Error: err}.Apply(req, resp) return } resp.Out.Header().Set("Location", url) resp.WriteHeader(http.StatusFound, "") }
func (c *MergedConfig) Bool(option string) (result, found bool) { result, err := c.config.Bool(c.section, option) if err == nil { return result, true } if _, ok := err.(config.OptionError); ok { return false, false } // If it wasn't an OptionError, it must have failed to parse. glog.Errorln("Failed to parse config option", option, "as bool:", err) return false, false }
// Listen registers for events within the given root directories (recursively). func (w *Watcher) Listen(listener Listener, roots ...string) { eventCh := make(chan notify.EventInfo, 100) // Walk through all files / directories under the root, adding each to watcher. for _, p := range roots { // is the directory / file a symlink? f, err := os.Lstat(p) if err == nil && f.Mode()&os.ModeSymlink == os.ModeSymlink { realPath, err := filepath.EvalSymlinks(p) if err != nil { panic(err) } p = realPath } fi, err := os.Stat(p) if err != nil { glog.Errorln("Failed to stat watched path", p, ":", err) continue } if fi.IsDir() { err = notify.Watch(p+string(filepath.Separator)+"...", eventCh, notify.All) } else { err = notify.Watch(p, eventCh, notify.All) } if err != nil { glog.Errorln("Failed to watch", p, ":", err) } } if w.eagerRebuildEnabled() { // Create goroutine to notify file changes in real time go w.NotifyWhenUpdated(listener, eventCh) } w.events = append(w.events, eventCh) w.listeners = append(w.listeners, listener) }
// SourceLines returns the template's source code. // A template of the given name must exist, or a panic results. func (loader *TemplateLoader) SourceLines(templateName string) []string { path, ok := loader.templatePaths[templateName] if !ok { panic("template not found: " + templateName) } fileBytes, err := ioutil.ReadFile(path) if err != nil { glog.Errorln("failed reading file", path, ":", err) return []string{} } // TODO: Better way to split into lines? return strings.Split(string(fileBytes), "\n") }
// Parse the line, and description from an error message like: // html/template:Application/Register.html:36: no such template "footer.html" func parseTemplateError(err error) (templateName string, line int, description string) { description = err.Error() i := regexp.MustCompile(`:\d+:`).FindStringIndex(description) if i != nil { line, err = strconv.Atoi(description[i[0]+1 : i[1]-1]) if err != nil { glog.Errorln("Failed to parse line number from error message:", err) } templateName = description[:i[0]] if colon := strings.Index(templateName, ":"); colon != -1 { templateName = templateName[colon+1:] } templateName = strings.TrimSpace(templateName) description = description[i[1]+1:] } return templateName, line, description }
// Pluralize, a helper for pluralizing words to correspond to data of dynamic length. // items - a slice of items, or an integer indicating how many items there are. // pluralOverrides - optional arguments specifying the output in the // singular and plural cases. by default "" and "s" func Pluralize(items interface{}, pluralOverrides ...string) string { singular, plural := "", "s" if len(pluralOverrides) >= 1 { singular = pluralOverrides[0] if len(pluralOverrides) == 2 { plural = pluralOverrides[1] } } switch v := reflect.ValueOf(items); v.Kind() { case reflect.Int: if items.(int) != 1 { return plural } case reflect.Slice: if v.Len() != 1 { return plural } default: glog.Errorln("pluralize: unexpected type: ", v) } return singular }
func (router *Router) Reverse(action string, argValues map[string]string) *ActionDefinition { NEXT_ROUTE: // Loop through the routes. for _, route := range router.Routes { if route.actionPattern == nil { continue } var matches []string = route.actionPattern.FindStringSubmatch(action) if len(matches) == 0 { continue } for i, match := range matches[1:] { argValues[route.actionPattern.SubexpNames()[i+1]] = match } // Create a lookup for the route args. routeArgs := make(map[string]*arg) for _, arg := range route.args { routeArgs[arg.name] = arg } // Enforce the constraints on the arg values. for argKey, argValue := range argValues { arg, ok := routeArgs[argKey] if ok && !arg.constraint.MatchString(argValue) { continue NEXT_ROUTE } } // Build up the URL. var queryValues url.Values = make(url.Values) // Handle optional trailing slashes (e.g. "/?") by removing the question mark. path := strings.Replace(route.Path, "?", "", -1) for argKey, argValue := range argValues { if _, ok := routeArgs[argKey]; ok { // If this arg goes into the path, put it in. path = regexp.MustCompile(`\{(<[^>]+>)?`+regexp.QuoteMeta(argKey)+`\}`). ReplaceAllString(path, url.QueryEscape(string(argValue))) } else { // Else, add it to the query string. queryValues.Set(argKey, argValue) } } // Calculate the final URL and Method url := path if len(queryValues) > 0 { url += "?" + queryValues.Encode() } method := route.Method star := false if route.Method == "*" { method = "GET" star = true } return &ActionDefinition{ Url: url, Method: method, Star: star, Action: action, Args: argValues, Host: "TODO", } } glog.Errorln("Failed to find reverse route:", action, argValues) return nil }
// 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 }