Changeset 24 in code


Ignore:
Timestamp:
Aug 30, 2015, 12:20:35 PM (10 years ago)
Author:
zaitsev.serge
Message:

fixed output file names in html pages, fixed amber function bindings, replaced print command with build, fixed plugin functions, implemented zs and exec functions

Location:
trunk
Files:
2 added
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/zs.go

    r23 r24  
    88        "log"
    99        "os"
    10         "os/exec"
    1110        "path"
    1211        "path/filepath"
     
    2625
    2726type Vars map[string]string
    28 
    29 // Splits a string in exactly two parts by delimiter
    30 // If no delimiter is found - the second string is be empty
    31 func split2(s, delim string) (string, string) {
    32         parts := strings.SplitN(s, delim, 2)
    33         if len(parts) == 2 {
    34                 return parts[0], parts[1]
    35         } else {
    36                 return parts[0], ""
    37         }
    38 }
     27type Funcs template.FuncMap
    3928
    4029// Parses markdown content. Returns parsed header variables and content
     
    7867
    7968// Use standard Go templates
    80 func render(s string, funcs template.FuncMap, vars Vars) (string, error) {
    81         f := template.FuncMap{}
     69func render(s string, funcs Funcs, vars Vars) (string, error) {
     70        f := Funcs{}
    8271        for k, v := range funcs {
    8372                f[k] = v
     
    8675                f[k] = varFunc(v)
    8776        }
    88         tmpl, err := template.New("").Funcs(f).Parse(s)
     77        // Plugin functions
     78        files, _ := ioutil.ReadDir(ZSDIR)
     79        for _, file := range files {
     80                if !file.IsDir() {
     81                        name := file.Name()
     82                        if !strings.HasSuffix(name, ".html") && !strings.HasSuffix(name, ".amber") {
     83                                f[strings.TrimSuffix(name, filepath.Ext(name))] = pluginFunc(name, vars)
     84                        }
     85                }
     86        }
     87
     88        tmpl, err := template.New("").Funcs(template.FuncMap(f)).Parse(s)
    8989        if err != nil {
    9090                return "", err
     
    9797}
    9898
    99 // Converts zs markdown variables into environment variables
    100 func env(vars Vars) []string {
    101         env := []string{"ZS=" + os.Args[0], "ZS_OUTDIR=" + PUBDIR}
    102         env = append(env, os.Environ()...)
    103         if vars != nil {
    104                 for k, v := range vars {
    105                         env = append(env, "ZS_"+strings.ToUpper(k)+"="+v)
    106                 }
    107         }
    108         return env
    109 }
    110 
    111 // Runs command with given arguments and variables, intercepts stderr and
    112 // redirects stdout into the given writer
    113 func run(cmd string, args []string, vars Vars, output io.Writer) error {
    114         var errbuf bytes.Buffer
    115         c := exec.Command(cmd, args...)
    116         c.Env = env(vars)
    117         c.Stdout = output
    118         c.Stderr = &errbuf
    119 
    120         err := c.Run()
    121 
    122         if errbuf.Len() > 0 {
    123                 log.Println("ERROR:", errbuf.String())
    124         }
    125 
    126         if err != nil {
    127                 return err
    128         }
    129         return nil
    130 }
    131 
    132 // Expands macro: either replacing it with the variable value, or
    133 // running the plugin command and replacing it with the command's output
    134 func eval(cmd []string, vars Vars) (string, error) {
    135         outbuf := bytes.NewBuffer(nil)
    136         err := run(path.Join(ZSDIR, cmd[0]), cmd[1:], vars, outbuf)
    137         if err != nil {
    138                 if _, ok := err.(*exec.ExitError); ok {
    139                         return "", err
    140                 }
    141                 outbuf = bytes.NewBuffer(nil)
    142                 err := run(cmd[0], cmd[1:], vars, outbuf)
    143                 // Return exit errors, but ignore if the command was not found
    144                 if _, ok := err.(*exec.ExitError); ok {
    145                         return "", err
    146                 }
    147         }
    148         return outbuf.String(), nil
    149 }
    150 
    15199// Renders markdown with the given layout into html expanding all the macros
    152 func buildMarkdown(path string, funcs template.FuncMap, vars Vars) error {
     100func buildMarkdown(path string, w io.Writer, funcs Funcs, vars Vars) error {
    153101        v, body, err := md(path, vars)
    154102        if err != nil {
     
    160108        }
    161109        v["content"] = string(blackfriday.MarkdownBasic([]byte(content)))
     110        if w == nil {
     111                out, err := os.Create(filepath.Join(PUBDIR, renameExt(path, "", ".html")))
     112                if err != nil {
     113                        return err
     114                }
     115                defer out.Close()
     116                w = out
     117        }
    162118        if strings.HasSuffix(v["layout"], ".amber") {
    163                 return buildAmber(filepath.Join(ZSDIR, v["layout"]),
    164                         renameExt(path, "", ".html"), funcs, v)
     119                return buildAmber(filepath.Join(ZSDIR, v["layout"]), w, funcs, v)
    165120        } else {
    166                 return buildPlain(filepath.Join(ZSDIR, v["layout"]),
    167                         renameExt(path, "", ".html"), funcs, v)
     121                return buildHTML(filepath.Join(ZSDIR, v["layout"]), w, funcs, v)
    168122        }
    169123}
    170124
    171125// Renders text file expanding all variable macros inside it
    172 func buildPlain(in, out string, funcs template.FuncMap, vars Vars) error {
    173         b, err := ioutil.ReadFile(in)
     126func buildHTML(path string, w io.Writer, funcs Funcs, vars Vars) error {
     127        b, err := ioutil.ReadFile(path)
    174128        if err != nil {
    175129                return err
     
    179133                return err
    180134        }
    181         output := filepath.Join(PUBDIR, out)
     135        output := filepath.Join(PUBDIR, path)
    182136        if s, ok := vars["output"]; ok {
    183137                output = s
     
    191145
    192146// Renders .amber file into .html
    193 func buildAmber(in, out string, funcs template.FuncMap, vars Vars) error {
     147func buildAmber(path string, w io.Writer, funcs Funcs, vars Vars) error {
    194148        a := amber.New()
    195         err := a.ParseFile(in)
     149        err := a.ParseFile(path)
    196150        if err != nil {
    197151                return err
     
    201155                return err
    202156        }
    203         //amber.FuncMap = amber.FuncMap
    204         f, err := os.Create(filepath.Join(PUBDIR, out))
     157        if w == nil {
     158                f, err := os.Create(filepath.Join(PUBDIR, renameExt(path, ".amber", ".html")))
     159                if err != nil {
     160                        return err
     161                }
     162                defer f.Close()
     163                w = f
     164        }
     165        return t.Execute(w, vars)
     166}
     167
     168// Compiles .gcss into .css
     169func buildGCSS(path string, w io.Writer) error {
     170        f, err := os.Open(path)
    205171        if err != nil {
    206172                return err
    207173        }
    208174        defer f.Close()
    209         return t.Execute(f, vars)
    210 }
    211 
    212 // Compiles .gcss into .css
    213 func buildGCSS(path string) error {
    214         f, err := os.Open(path)
    215         if err != nil {
    216                 return err
    217         }
    218         s := strings.TrimSuffix(path, ".gcss") + ".css"
    219         css, err := os.Create(filepath.Join(PUBDIR, s))
    220         if err != nil {
    221                 return err
    222         }
    223 
    224         defer f.Close()
    225         defer css.Close()
    226 
    227         _, err = gcss.Compile(css, f)
     175
     176        if w == nil {
     177                s := strings.TrimSuffix(path, ".gcss") + ".css"
     178                css, err := os.Create(filepath.Join(PUBDIR, s))
     179                if err != nil {
     180                        return err
     181                }
     182                defer css.Close()
     183                w = css
     184        }
     185        _, err = gcss.Compile(w, f)
    228186        return err
    229187}
    230188
    231 // Copies file from working directory into public directory
    232 func copyFile(path string) (err error) {
    233         var in, out *os.File
    234         if in, err = os.Open(path); err == nil {
    235                 defer in.Close()
    236                 if out, err = os.Create(filepath.Join(PUBDIR, path)); err == nil {
     189// Copies file as is from path to writer
     190func buildRaw(path string, w io.Writer) error {
     191        in, err := os.Open(path)
     192        if err != nil {
     193                return err
     194        }
     195        defer in.Close()
     196        if w == nil {
     197                if out, err := os.Create(filepath.Join(PUBDIR, path)); err != nil {
     198                        return err
     199                } else {
    237200                        defer out.Close()
    238                         _, err = io.Copy(out, in)
    239                 }
    240         }
     201                        w = out
     202                }
     203        }
     204        _, err = io.Copy(w, in)
    241205        return err
    242206}
    243207
    244 func varFunc(s string) func() string {
    245         return func() string {
    246                 return s
    247         }
    248 }
    249 
    250 func pluginFunc(cmd string) func() string {
    251         return func() string {
    252                 return "Not implemented yet"
    253         }
    254 }
    255 
    256 func createFuncs() template.FuncMap {
    257         // Builtin functions
    258         funcs := template.FuncMap{
    259                 "exec": func(s ...string) string {
    260                         // Run external command with arguments
    261                         return ""
    262                 },
    263                 "zs": func(args ...string) string {
    264                         // Run zs with arguments
    265                         return ""
    266                 },
    267         }
    268         // Plugin functions
    269         files, _ := ioutil.ReadDir(ZSDIR)
    270         for _, f := range files {
    271                 if !f.IsDir() {
    272                         name := f.Name()
    273                         if !strings.HasSuffix(name, ".html") && !strings.HasSuffix(name, ".amber") {
    274                                 funcs[strings.TrimSuffix(name, filepath.Ext(name))] = pluginFunc(name)
    275                         }
    276                 }
    277         }
    278         return funcs
    279 }
    280 
    281 func renameExt(path, from, to string) string {
    282         if from == "" {
    283                 from = filepath.Ext(path)
    284         }
    285         return strings.TrimSuffix(path, from) + to
    286 }
    287 
    288 func globals() Vars {
    289         vars := Vars{}
    290         for _, e := range os.Environ() {
    291                 pair := strings.Split(e, "=")
    292                 if strings.HasPrefix(pair[0], "ZS_") {
    293                         vars[strings.ToLower(pair[0][3:])] = pair[1]
    294                 }
    295         }
    296         return vars
    297 }
    298 
    299 func buildAll(once bool) {
     208func build(path string, w io.Writer, funcs Funcs, vars Vars) error {
     209        ext := filepath.Ext(path)
     210        if ext == ".md" || ext == ".mkd" {
     211                return buildMarkdown(path, w, funcs, vars)
     212        } else if ext == ".html" || ext == ".xml" {
     213                return buildHTML(path, w, funcs, vars)
     214        } else if ext == ".amber" {
     215                return buildAmber(path, w, funcs, vars)
     216        } else if ext == ".gcss" {
     217                return buildGCSS(path, w)
     218        } else {
     219                return buildRaw(path, w)
     220        }
     221}
     222
     223func buildAll(watch bool) {
    300224        lastModified := time.Unix(0, 0)
    301225        modified := false
     
    304228        for {
    305229                os.Mkdir(PUBDIR, 0755)
    306                 funcs := createFuncs()
     230                funcs := builtins()
    307231                err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
    308232                        // ignore hidden files and directories
     
    321245                                        modified = true
    322246                                }
    323                                 ext := filepath.Ext(path)
    324                                 if ext == ".md" || ext == ".mkd" {
    325                                         log.Println("md: ", path)
    326                                         return buildMarkdown(path, funcs, vars)
    327                                 } else if ext == ".html" || ext == ".xml" {
    328                                         log.Println("html: ", path)
    329                                         return buildPlain(path, path, funcs, vars)
    330                                 } else if ext == ".amber" {
    331                                         log.Println("html: ", path)
    332                                         return buildAmber(path, renameExt(path, ".amber", ".html"), funcs, vars)
    333                                 } else if ext == ".gcss" {
    334                                         log.Println("css: ", path)
    335                                         return buildGCSS(path)
    336                                 } else {
    337                                         log.Println("raw: ", path)
    338                                         return copyFile(path)
    339                                 }
     247                                log.Println("build: ", path)
     248                                return build(path, nil, funcs, vars)
    340249                        }
    341250                        return nil
     
    350259                        modified = false
    351260                }
     261                if !watch {
     262                        break
     263                }
    352264                lastModified = time.Now()
    353                 if once {
    354                         break
    355                 }
    356265                time.Sleep(1 * time.Second)
    357266        }
     
    367276        switch cmd {
    368277        case "build":
     278                buildAll(false)
     279        case "watch":
    369280                buildAll(true)
    370         case "watch":
    371                 buildAll(false) // pass duration
     281        case "print":
     282                if len(args) != 1 {
     283                        fmt.Println("ERROR: filename expected")
     284                } else {
     285                        build(args[0], os.Stdout, builtins(), globals())
     286                }
    372287        case "var":
    373                 if len(args) == 0 {
    374                         log.Println("ERROR: filename expected")
    375                         return
    376                 }
    377                 if vars, _, err := md(args[0], globals()); err == nil {
    378                         if len(args) > 1 {
    379                                 for _, a := range args[1:] {
    380                                         fmt.Println(vars[a])
    381                                 }
    382                         } else {
    383                                 for k, v := range vars {
    384                                         fmt.Println(k + ":" + v)
    385                                 }
    386                         }
    387                 } else {
    388                         log.Println("ERROR:", err)
    389                 }
     288                fmt.Println(Var(args))
     289        case "lorem":
     290                fmt.Println(Lorem(args))
     291        case "dateparse":
     292                fmt.Println(DateParse(args))
     293        case "datefmt":
     294                fmt.Println(DateFmt(args))
    390295        default:
    391296                err := run(path.Join(ZSDIR, cmd), args, Vars{}, os.Stdout)
  • trunk/zs_test.go

    r20 r24  
    77        "log"
    88        "os"
    9         "os/exec"
    109        "strings"
    1110        "testing"
    12         "text/template"
    1311)
    1412
     
    7977func TestRender(t *testing.T) {
    8078        vars := map[string]string{"foo": "bar"}
    81         funcs := template.FuncMap{
     79        funcs := Funcs{
    8280                "greet": func(s ...string) string {
    8381                        if len(s) == 0 {
     
    139137}
    140138
    141 func TestEvalCommand(t *testing.T) {
    142         s, err := eval([]string{"echo", "hello"}, map[string]string{})
    143         if err != nil {
    144                 t.Error(err)
    145         }
    146         if s != "hello\n" {
    147                 t.Error(s)
    148         }
    149         _, err = eval([]string{"cat", "bogus/file"}, map[string]string{})
    150         if _, ok := err.(*exec.ExitError); !ok {
    151                 t.Error("expected ExitError")
    152         }
    153         _, err = eval([]string{"missing command"}, map[string]string{})
    154         if err != nil {
    155                 t.Error("missing command should be ignored")
    156         }
    157 }
    158 
    159139func TestHelperProcess(*testing.T) {
    160140        if os.Getenv("ZS_HELPER") != "1" {
Note: See TracChangeset for help on using the changeset viewer.