]> Nishi Git Mirror - aya.git/commitdiff
started migration to go templates
authorSerge A. Zaitsev <zaitsev.serge@gmail.com>
Sat, 29 Aug 2015 16:46:05 +0000 (18:46 +0200)
committerSerge A. Zaitsev <zaitsev.serge@gmail.com>
Sat, 29 Aug 2015 16:46:05 +0000 (18:46 +0200)
testdata/page/index.html
zs.go
zs_test.go

index ee494245be671b35adce25595771b22ee9dce41a..86a50258fa37c4711291cf261842ac23a2216fbf 100644 (file)
@@ -1,5 +1,5 @@
 <html>
        <body>
-               <h1>{{ echo Hello }}</h1>
+               <h1>{{ println "Hello" }}</h1>
        </body>
 </html>
diff --git a/zs.go b/zs.go
index 171f8674ac16c8c9cca2361e4833cb45ed8c9bea..90dc3d3a347ed22509daa18b457e07c14cf1a6e5 100644 (file)
--- a/zs.go
+++ b/zs.go
@@ -11,6 +11,7 @@ import (
        "path"
        "path/filepath"
        "strings"
+       "text/template"
        "time"
 
        "github.com/eknkc/amber"
@@ -25,8 +26,6 @@ const (
 
 type Vars map[string]string
 
-type EvalFn func(args []string, vars Vars) (string, error)
-
 // Splits a string in exactly two parts by delimiter
 // If no delimiter is found - the second string is be empty
 func split2(s, delim string) (string, string) {
@@ -66,37 +65,24 @@ func md(path string) (Vars, string, error) {
        return v, body, nil
 }
 
-func render(s string, vars Vars, eval EvalFn) (string, error) {
-       delim_open := "{{"
-       delim_close := "}}"
-
-       out := bytes.NewBuffer(nil)
-       for {
-               if from := strings.Index(s, delim_open); from == -1 {
-                       out.WriteString(s)
-                       return out.String(), nil
-               } else {
-                       if to := strings.Index(s, delim_close); to == -1 {
-                               return "", fmt.Errorf("Close delim not found")
-                       } else {
-                               out.WriteString(s[:from])
-                               cmd := s[from+len(delim_open) : to]
-                               s = s[to+len(delim_close):]
-                               m := strings.Fields(cmd)
-                               if len(m) == 1 {
-                                       if v, ok := vars[m[0]]; ok {
-                                               out.WriteString(v)
-                                               continue
-                                       }
-                               }
-                               if res, err := eval(m, vars); err == nil {
-                                       out.WriteString(res)
-                               } else {
-                                       log.Println(err) // silent
-                               }
-                       }
-               }
+// Use standard Go templates
+func render(s string, funcs template.FuncMap, vars Vars) (string, error) {
+       f := template.FuncMap{}
+       for k, v := range funcs {
+               f[k] = v
+       }
+       for k, v := range vars {
+               f[k] = varFunc(v)
+       }
+       tmpl, err := template.New("").Funcs(f).Parse(s)
+       if err != nil {
+               return "", err
        }
+       out := &bytes.Buffer{}
+       if err := tmpl.Execute(out, vars); err != nil {
+               return "", err
+       }
+       return string(out.Bytes()), nil
 }
 
 // Converts zs markdown variables into environment variables
@@ -132,6 +118,8 @@ func run(cmd string, args []string, vars Vars, output io.Writer) error {
        return nil
 }
 
+// Expands macro: either replacing it with the variable value, or
+// running the plugin command and replacing it with the command's output
 func eval(cmd []string, vars Vars) (string, error) {
        outbuf := bytes.NewBuffer(nil)
        err := run(path.Join(ZSDIR, cmd[0]), cmd[1:], vars, outbuf)
@@ -149,25 +137,31 @@ func eval(cmd []string, vars Vars) (string, error) {
        return outbuf.String(), nil
 }
 
-func buildMarkdown(path string) error {
+// Renders markdown with the given layout into html expanding all the macros
+func buildMarkdown(path string, funcs template.FuncMap, vars Vars) error {
        v, body, err := md(path)
        if err != nil {
                return err
        }
-       content, err := render(body, v, eval)
+       content, err := render(body, funcs, v)
        if err != nil {
                return err
        }
        v["content"] = string(blackfriday.MarkdownBasic([]byte(content)))
-       return buildPlain(filepath.Join(ZSDIR, v["layout"]), v)
+       if strings.HasSuffix(v["layout"], ".amber") {
+               return buildAmber(filepath.Join(ZSDIR, v["layout"]), funcs, v)
+       } else {
+               return buildPlain(filepath.Join(ZSDIR, v["layout"]), funcs, v)
+       }
 }
 
-func buildPlain(path string, vars Vars) error {
+// Renders text file expanding all variable macros inside it
+func buildPlain(path string, funcs template.FuncMap, vars Vars) error {
        b, err := ioutil.ReadFile(path)
        if err != nil {
                return err
        }
-       content, err := render(string(b), vars, eval)
+       content, err := render(string(b), funcs, vars)
        if err != nil {
                return err
        }
@@ -182,26 +176,8 @@ func buildPlain(path string, vars Vars) error {
        return nil
 }
 
-func buildGCSS(path string) error {
-       f, err := os.Open(path)
-       if err != nil {
-               return err
-       }
-       s := strings.TrimSuffix(path, ".gcss") + ".css"
-       log.Println(s)
-       css, err := os.Create(filepath.Join(PUBDIR, s))
-       if err != nil {
-               return err
-       }
-
-       defer f.Close()
-       defer css.Close()
-
-       _, err = gcss.Compile(css, f)
-       return err
-}
-
-func buildAmber(path string, vars Vars) error {
+// Renders .amber file into .html
+func buildAmber(path string, funcs template.FuncMap, vars Vars) error {
        a := amber.New()
        err := a.ParseFile(path)
        if err != nil {
@@ -221,6 +197,26 @@ func buildAmber(path string, vars Vars) error {
        return t.Execute(f, vars)
 }
 
+// Compiles .gcss into .css
+func buildGCSS(path string) error {
+       f, err := os.Open(path)
+       if err != nil {
+               return err
+       }
+       s := strings.TrimSuffix(path, ".gcss") + ".css"
+       css, err := os.Create(filepath.Join(PUBDIR, s))
+       if err != nil {
+               return err
+       }
+
+       defer f.Close()
+       defer css.Close()
+
+       _, err = gcss.Compile(css, f)
+       return err
+}
+
+// Copies file from working directory into public directory
 func copyFile(path string) (err error) {
        var in, out *os.File
        if in, err = os.Open(path); err == nil {
@@ -233,11 +229,48 @@ func copyFile(path string) (err error) {
        return err
 }
 
+func varFunc(s string) func() string {
+       return func() string {
+               return s
+       }
+}
+
+func pluginFunc(cmd string) func() string {
+       return func() string {
+               return "Not implemented yet"
+       }
+}
+
+func createFuncs() template.FuncMap {
+       // Builtin functions
+       funcs := template.FuncMap{}
+       // Plugin functions
+       files, _ := ioutil.ReadDir(ZSDIR)
+       for _, f := range files {
+               if !f.IsDir() {
+                       name := f.Name()
+                       if !strings.HasSuffix(name, ".html") && !strings.HasSuffix(name, ".amber") {
+                               funcs[strings.TrimSuffix(name, filepath.Ext(name))] = pluginFunc(name)
+                       }
+               }
+       }
+       return funcs
+}
+
 func buildAll(once bool) {
        lastModified := time.Unix(0, 0)
        modified := false
+       // Convert env variables into zs global variables
+       globals := Vars{}
+       for _, e := range os.Environ() {
+               pair := strings.Split(e, "=")
+               if strings.HasPrefix(pair[0], "ZS_") {
+                       globals[strings.ToLower(pair[0][3:])] = pair[1]
+               }
+       }
        for {
                os.Mkdir(PUBDIR, 0755)
+               funcs := createFuncs()
                err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
                        // ignore hidden files and directories
                        if filepath.Base(path)[0] == '.' || strings.HasPrefix(path, ".") {
@@ -250,19 +283,20 @@ func buildAll(once bool) {
                        } else if info.ModTime().After(lastModified) {
                                if !modified {
                                        // About to be modified, so run pre-build hook
+                                       // FIXME on windows it might not work well
                                        run(filepath.Join(ZSDIR, "pre"), []string{}, nil, nil)
                                        modified = true
                                }
                                ext := filepath.Ext(path)
                                if ext == ".md" || ext == ".mkd" {
                                        log.Println("md: ", path)
-                                       return buildMarkdown(path)
+                                       return buildMarkdown(path, funcs, globals)
                                } else if ext == ".html" || ext == ".xml" {
                                        log.Println("html: ", path)
-                                       return buildPlain(path, Vars{})
+                                       return buildPlain(path, funcs, globals)
                                } else if ext == ".amber" {
                                        log.Println("html: ", path)
-                                       return buildAmber(path, Vars{})
+                                       return buildAmber(path, funcs, globals)
                                } else if ext == ".gcss" {
                                        log.Println("css: ", path)
                                        return buildGCSS(path)
@@ -278,6 +312,7 @@ func buildAll(once bool) {
                }
                if modified {
                        // Something was modified, so post-build hook
+                       // FIXME on windows it might not work well
                        run(filepath.Join(ZSDIR, "post"), []string{}, nil, nil)
                        modified = false
                }
index b74cbe246ab10aa05a1fb43b9ba354666eb74677..e4eed70c55da920d9488075a4edacb39961178d5 100644 (file)
@@ -9,6 +9,7 @@ import (
        "os/exec"
        "strings"
        "testing"
+       "text/template"
 )
 
 func TestSplit2(t *testing.T) {
@@ -76,23 +77,29 @@ this: is a content`))
 }
 
 func TestRender(t *testing.T) {
-       eval := func(a []string, vars Vars) (string, error) {
-               return "hello", nil
-       }
        vars := map[string]string{"foo": "bar"}
+       funcs := template.FuncMap{
+               "greet": func(s ...string) string {
+                       if len(s) == 0 {
+                               return "hello"
+                       } else {
+                               return "hello " + strings.Join(s, " ")
+                       }
+               },
+       }
 
-       if s, err := render("plain text", vars, eval); err != nil || s != "plain text" {
-               t.Error()
+       if s, err := render("plain text", funcs, vars); err != nil || s != "plain text" {
+               t.Error(s, err)
        }
-       if s, err := render("a {{greet}} text", vars, eval); err != nil || s != "a hello text" {
-               t.Error()
+       if s, err := render("a {{greet}} text", funcs, vars); err != nil || s != "a hello text" {
+               t.Error(s, err)
        }
-       if s, err := render("{{greet}} x{{foo}}z", vars, eval); err != nil || s != "hello xbarz" {
-               t.Error()
+       if s, err := render("{{greet}} x{{foo}}z", funcs, vars); err != nil || s != "hello xbarz" {
+               t.Error(s, err)
        }
        // Test error case
-       if s, err := render("a {{greet text ", vars, eval); err == nil || len(s) != 0 {
-               t.Error()
+       if s, err := render("a {{greet text ", funcs, vars); err == nil || len(s) != 0 {
+               t.Error(s, err)
        }
 }