Changeset 19 in code


Ignore:
Timestamp:
Aug 29, 2015, 4:46:05 PM (10 years ago)
Author:
zaitsev.serge
Message:

started migration to go templates

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/testdata/page/index.html

    r17 r19  
    11<html>
    22        <body>
    3                 <h1>{{ echo Hello }}</h1>
     3                <h1>{{ println "Hello" }}</h1>
    44        </body>
    55</html>
  • trunk/zs.go

    r18 r19  
    1212        "path/filepath"
    1313        "strings"
     14        "text/template"
    1415        "time"
    1516
     
    2526
    2627type Vars map[string]string
    27 
    28 type EvalFn func(args []string, vars Vars) (string, error)
    2928
    3029// Splits a string in exactly two parts by delimiter
     
    6766}
    6867
    69 func render(s string, vars Vars, eval EvalFn) (string, error) {
    70         delim_open := "{{"
    71         delim_close := "}}"
    72 
    73         out := bytes.NewBuffer(nil)
    74         for {
    75                 if from := strings.Index(s, delim_open); from == -1 {
    76                         out.WriteString(s)
    77                         return out.String(), nil
    78                 } else {
    79                         if to := strings.Index(s, delim_close); to == -1 {
    80                                 return "", fmt.Errorf("Close delim not found")
    81                         } else {
    82                                 out.WriteString(s[:from])
    83                                 cmd := s[from+len(delim_open) : to]
    84                                 s = s[to+len(delim_close):]
    85                                 m := strings.Fields(cmd)
    86                                 if len(m) == 1 {
    87                                         if v, ok := vars[m[0]]; ok {
    88                                                 out.WriteString(v)
    89                                                 continue
    90                                         }
    91                                 }
    92                                 if res, err := eval(m, vars); err == nil {
    93                                         out.WriteString(res)
    94                                 } else {
    95                                         log.Println(err) // silent
    96                                 }
    97                         }
    98                 }
    99         }
     68// Use standard Go templates
     69func render(s string, funcs template.FuncMap, vars Vars) (string, error) {
     70        f := template.FuncMap{}
     71        for k, v := range funcs {
     72                f[k] = v
     73        }
     74        for k, v := range vars {
     75                f[k] = varFunc(v)
     76        }
     77        tmpl, err := template.New("").Funcs(f).Parse(s)
     78        if err != nil {
     79                return "", err
     80        }
     81        out := &bytes.Buffer{}
     82        if err := tmpl.Execute(out, vars); err != nil {
     83                return "", err
     84        }
     85        return string(out.Bytes()), nil
    10086}
    10187
     
    133119}
    134120
     121// Expands macro: either replacing it with the variable value, or
     122// running the plugin command and replacing it with the command's output
    135123func eval(cmd []string, vars Vars) (string, error) {
    136124        outbuf := bytes.NewBuffer(nil)
     
    150138}
    151139
    152 func buildMarkdown(path string) error {
     140// Renders markdown with the given layout into html expanding all the macros
     141func buildMarkdown(path string, funcs template.FuncMap, vars Vars) error {
    153142        v, body, err := md(path)
    154143        if err != nil {
    155144                return err
    156145        }
    157         content, err := render(body, v, eval)
     146        content, err := render(body, funcs, v)
    158147        if err != nil {
    159148                return err
    160149        }
    161150        v["content"] = string(blackfriday.MarkdownBasic([]byte(content)))
    162         return buildPlain(filepath.Join(ZSDIR, v["layout"]), v)
    163 }
    164 
    165 func buildPlain(path string, vars Vars) error {
     151        if strings.HasSuffix(v["layout"], ".amber") {
     152                return buildAmber(filepath.Join(ZSDIR, v["layout"]), funcs, v)
     153        } else {
     154                return buildPlain(filepath.Join(ZSDIR, v["layout"]), funcs, v)
     155        }
     156}
     157
     158// Renders text file expanding all variable macros inside it
     159func buildPlain(path string, funcs template.FuncMap, vars Vars) error {
    166160        b, err := ioutil.ReadFile(path)
    167161        if err != nil {
    168162                return err
    169163        }
    170         content, err := render(string(b), vars, eval)
     164        content, err := render(string(b), funcs, vars)
    171165        if err != nil {
    172166                return err
     
    183177}
    184178
    185 func buildGCSS(path string) error {
    186         f, err := os.Open(path)
    187         if err != nil {
    188                 return err
    189         }
    190         s := strings.TrimSuffix(path, ".gcss") + ".css"
    191         log.Println(s)
    192         css, err := os.Create(filepath.Join(PUBDIR, s))
    193         if err != nil {
    194                 return err
    195         }
    196 
    197         defer f.Close()
    198         defer css.Close()
    199 
    200         _, err = gcss.Compile(css, f)
    201         return err
    202 }
    203 
    204 func buildAmber(path string, vars Vars) error {
     179// Renders .amber file into .html
     180func buildAmber(path string, funcs template.FuncMap, vars Vars) error {
    205181        a := amber.New()
    206182        err := a.ParseFile(path)
     
    222198}
    223199
     200// Compiles .gcss into .css
     201func buildGCSS(path string) error {
     202        f, err := os.Open(path)
     203        if err != nil {
     204                return err
     205        }
     206        s := strings.TrimSuffix(path, ".gcss") + ".css"
     207        css, err := os.Create(filepath.Join(PUBDIR, s))
     208        if err != nil {
     209                return err
     210        }
     211
     212        defer f.Close()
     213        defer css.Close()
     214
     215        _, err = gcss.Compile(css, f)
     216        return err
     217}
     218
     219// Copies file from working directory into public directory
    224220func copyFile(path string) (err error) {
    225221        var in, out *os.File
     
    234230}
    235231
     232func varFunc(s string) func() string {
     233        return func() string {
     234                return s
     235        }
     236}
     237
     238func pluginFunc(cmd string) func() string {
     239        return func() string {
     240                return "Not implemented yet"
     241        }
     242}
     243
     244func createFuncs() template.FuncMap {
     245        // Builtin functions
     246        funcs := template.FuncMap{}
     247        // Plugin functions
     248        files, _ := ioutil.ReadDir(ZSDIR)
     249        for _, f := range files {
     250                if !f.IsDir() {
     251                        name := f.Name()
     252                        if !strings.HasSuffix(name, ".html") && !strings.HasSuffix(name, ".amber") {
     253                                funcs[strings.TrimSuffix(name, filepath.Ext(name))] = pluginFunc(name)
     254                        }
     255                }
     256        }
     257        return funcs
     258}
     259
    236260func buildAll(once bool) {
    237261        lastModified := time.Unix(0, 0)
    238262        modified := false
     263        // Convert env variables into zs global variables
     264        globals := Vars{}
     265        for _, e := range os.Environ() {
     266                pair := strings.Split(e, "=")
     267                if strings.HasPrefix(pair[0], "ZS_") {
     268                        globals[strings.ToLower(pair[0][3:])] = pair[1]
     269                }
     270        }
    239271        for {
    240272                os.Mkdir(PUBDIR, 0755)
     273                funcs := createFuncs()
    241274                err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
    242275                        // ignore hidden files and directories
     
    251284                                if !modified {
    252285                                        // About to be modified, so run pre-build hook
     286                                        // FIXME on windows it might not work well
    253287                                        run(filepath.Join(ZSDIR, "pre"), []string{}, nil, nil)
    254288                                        modified = true
     
    257291                                if ext == ".md" || ext == ".mkd" {
    258292                                        log.Println("md: ", path)
    259                                         return buildMarkdown(path)
     293                                        return buildMarkdown(path, funcs, globals)
    260294                                } else if ext == ".html" || ext == ".xml" {
    261295                                        log.Println("html: ", path)
    262                                         return buildPlain(path, Vars{})
     296                                        return buildPlain(path, funcs, globals)
    263297                                } else if ext == ".amber" {
    264298                                        log.Println("html: ", path)
    265                                         return buildAmber(path, Vars{})
     299                                        return buildAmber(path, funcs, globals)
    266300                                } else if ext == ".gcss" {
    267301                                        log.Println("css: ", path)
     
    279313                if modified {
    280314                        // Something was modified, so post-build hook
     315                        // FIXME on windows it might not work well
    281316                        run(filepath.Join(ZSDIR, "post"), []string{}, nil, nil)
    282317                        modified = false
  • trunk/zs_test.go

    r18 r19  
    1010        "strings"
    1111        "testing"
     12        "text/template"
    1213)
    1314
     
    7778
    7879func TestRender(t *testing.T) {
    79         eval := func(a []string, vars Vars) (string, error) {
    80                 return "hello", nil
     80        vars := map[string]string{"foo": "bar"}
     81        funcs := template.FuncMap{
     82                "greet": func(s ...string) string {
     83                        if len(s) == 0 {
     84                                return "hello"
     85                        } else {
     86                                return "hello " + strings.Join(s, " ")
     87                        }
     88                },
    8189        }
    82         vars := map[string]string{"foo": "bar"}
    8390
    84         if s, err := render("plain text", vars, eval); err != nil || s != "plain text" {
    85                 t.Error()
     91        if s, err := render("plain text", funcs, vars); err != nil || s != "plain text" {
     92                t.Error(s, err)
    8693        }
    87         if s, err := render("a {{greet}} text", vars, eval); err != nil || s != "a hello text" {
    88                 t.Error()
     94        if s, err := render("a {{greet}} text", funcs, vars); err != nil || s != "a hello text" {
     95                t.Error(s, err)
    8996        }
    90         if s, err := render("{{greet}} x{{foo}}z", vars, eval); err != nil || s != "hello xbarz" {
    91                 t.Error()
     97        if s, err := render("{{greet}} x{{foo}}z", funcs, vars); err != nil || s != "hello xbarz" {
     98                t.Error(s, err)
    9299        }
    93100        // Test error case
    94         if s, err := render("a {{greet text ", vars, eval); err == nil || len(s) != 0 {
    95                 t.Error()
     101        if s, err := render("a {{greet text ", funcs, vars); err == nil || len(s) != 0 {
     102                t.Error(s, err)
    96103        }
    97104}
Note: See TracChangeset for help on using the changeset viewer.