-
Notifications
You must be signed in to change notification settings - Fork 0
/
ddoc.go
111 lines (93 loc) · 2.79 KB
/
ddoc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package ddoc
import (
"bytes"
"errors"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"text/template"
)
type DesignDoc struct {
ID string `json:"_id"`
Rev string `json:"_rev,omitempty"`
Language string `json:"language"`
Views map[string]interface{} `json:"views"`
}
// type View is used to fill the design doc template
// with javascript files
// it is not marshaled to or from JSON itself
type View struct {
Name string
MapFunction string
ReduceFunction string
}
// views is passed to the template
var views []View
// BuildDesignDocument takes a design document name (do not include _design/)
// and a file path where javascript views can be found; these views should be
// map.js (TODO: reduce.js) files under subdirectories named after the view
//
// Example:
// path/views/customers/map.js
//
// From these, it composes a CouchDB design document having the map and reduce
// functions as named views
func Build(name, path string) ([]byte, error) {
// Error checking
if strings.ContainsRune(name, '/') {
return nil, errors.New("Do not include / in design document name")
} else if strings.Contains(name, "_design") {
return nil, errors.New("Do not begin design document name with _design")
}
ddocTemplate := filepath.Join(path, "ddoc.tmpl")
if _, err := os.Stat(ddocTemplate); os.IsNotExist(err) {
// path/ddoc.tmpl does not exist
return nil, err
}
ddocID := "_design/" + name
//ddoc := &DesignDoc{ID: ddocID}
_ = &DesignDoc{ID: ddocID}
// Glob files
mapFiles := filepath.Join(path, "views/*/", "map.js")
matches, err := filepath.Glob(mapFiles)
if err != nil {
// only if malformed pattern
log.Fatalln(err)
}
if len(matches) < 1 {
log.Fatalln("No javascript views found in " + path)
}
// Build views
for i := 0; i < len(matches); i++ {
v := View{}
// take the last directory from the path by removing filename (.Dir)
// and then taking the last element (.Base)
v.Name = filepath.Base(filepath.Dir(matches[i]))
// read map.js
filebuf, err := ioutil.ReadFile(matches[i])
if err != nil {
return nil, err
}
mapFunc := string(filebuf)
mapFunc = strings.Replace(mapFunc, "\n", "\\n", -1) // replace newline with literal \n
mapFunc = strings.Replace(mapFunc, "\r", "", -1) // replace carriage ret with nothing
mapFunc = strings.Replace(mapFunc, "\t", " ", -1) // JSON spec says no control characters
// and in fact json.Unmarshal will barf on tab
v.MapFunction = string(mapFunc)
v.ReduceFunction = "_count"
// Append to 'views' slice
views = append(views, v)
}
// Fill template
t, err := template.ParseFiles(ddocTemplate)
if err != nil {
log.Fatalln(err)
}
// print results
buf := make([]byte, 0, 8192) // byte slice with capacity of 8k
b := bytes.NewBuffer(buf)
t.Execute(b, views)
return b.Bytes(), nil
}