diff --git a/README.md b/README.md index d555a6672..fa3ee643b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,72 @@ $ echo "Hello, {{.Env.USER}}" | gomplate Hello, hairyhenderson ``` +#### About `.Env` + +You can easily access environment variables with `.Env`, but there's a catch: +if you try to reference an environment variable that doesn't exist, parsing +will fail and `gomplate` will exit with an error condition. + +Sometimes, this behaviour is desired; if the output is unusable without certain strings, this is a sure way to know that variables are missing! + +If you want different behaviour, try `getenv` (below). + +### Built-in functions + +In addition to all of the functions and operators that the [Go template](https://golang.org/pkg/text/template/) +language provides (`if`, `else`, `eq`, `and`, `or`, `range`, etc...), there are +some additional functions baked in to `gomplate`: + +#### `getenv` + +Exposes the [os.Getenv](https://golang.org/pkg/os/#Getenv) function. + +This is a more forgiving alternative to using `.Env`, since missing keys will +return an empty string. + +##### Example + +```console +$ echo 'Hello, {{getenv "USER"}}' | gomplate +Hello, hairyhenderson +``` +#### `bool` + +Converts a true-ish string to a boolean. Can be used to simplify conditional statements based on environment variables or other text input. + +##### Example + +_`input.tmpl`:_ +``` +{{if bool (getenv "FOO")}}foo{{else}}bar{{end}} +``` + +```console +$ gomplate < input.tmpl +bar +$ FOO=true gomplate < input.tmpl +foo +``` + +### Some more complex examples + +##### Variable assignment and `if`/`else` + +_`input.tmpl`:_ +``` +{{ $u := getenv "USER" }} +{{ if eq $u "root" }}You are root!{{else}}You are not root :({{end}} +``` + +```console +$ gomplate < input.tmpl +You are not root :( +$ sudo gomplate < input.tmpl +You are root! +``` + +_Note:_ it's important for the `if`/`else`/`end` keywords to appear on the same line, or else `gomplate` will not be able to parse the pipeline properly + ## License [The MIT License](http://opensource.org/licenses/MIT) diff --git a/main.go b/main.go index a6537b321..c5ed4f8ae 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,13 @@ package main import ( - "bufio" "flag" "fmt" "io" + "io/ioutil" "log" "os" + "strconv" "text/template" ) @@ -23,25 +24,49 @@ func init() { } } +// Getenv retrieves the value of the environment variable named by the key. +// It returns the value, which will be empty if the variable is not present. +func Getenv(key string) string { + return os.Getenv(key) +} + +// Bool converts a string to a boolean value, using strconv.ParseBool under the covers. +// Possible true values are: 1, t, T, TRUE, true, True +// All other values are considered false. +func Bool(in string) bool { + if b, err := strconv.ParseBool(in); err == nil { + return b + } + return false +} + +var funcMap = template.FuncMap{ + "Getenv": Getenv, + "getenv": Getenv, + "Bool": Bool, + "bool": Bool, +} + func createTemplate() *template.Template { - return template.New("template").Option("missingkey=error") + return template.New("template").Funcs(funcMap).Option("missingkey=error") } // RunTemplate - func RunTemplate(in io.Reader, out io.Writer) { - s := bufio.NewScanner(in) context := &Context{} - for s.Scan() { - tmpl, err := createTemplate().Parse(s.Text()) - if err != nil { - log.Fatalf("Line %q: %v\n", s.Text(), err) - } - - if err := tmpl.Execute(out, context); err != nil { - panic(err) - } - out.Write([]byte("\n")) + text, err := ioutil.ReadAll(in) + if err != nil { + log.Fatalf("Read failed!\n%v\n", err) + } + tmpl, err := createTemplate().Parse(string(text)) + if err != nil { + log.Fatalf("Line %q: %v\n", string(text), err) + } + + if err := tmpl.Execute(out, context); err != nil { + panic(err) } + out.Write([]byte("\n")) } func main() { diff --git a/main_test.go b/main_test.go new file mode 100644 index 000000000..36a1460b5 --- /dev/null +++ b/main_test.go @@ -0,0 +1,40 @@ +package main + +import ( + "bytes" + "os" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func testTemplate(template string) string { + in := strings.NewReader(template) + var out bytes.Buffer + RunTemplate(in, &out) + return strings.TrimSpace(out.String()) +} + +func TestGetenv(t *testing.T) { + assert.Empty(t, Getenv("FOOBARBAZ")) + assert.Empty(t, testTemplate(`{{getenv "BLAHBLAHBLAH"}}`)) + assert.Equal(t, Getenv("USER"), os.Getenv("USER")) + assert.Equal(t, os.Getenv("USER"), testTemplate(`{{getenv "USER"}}`)) +} + +func TestBool(t *testing.T) { + assert.False(t, Bool("")) + assert.False(t, Bool("asdf")) + assert.False(t, Bool("1234")) + assert.False(t, Bool("False")) + assert.False(t, Bool("0")) + assert.False(t, Bool("false")) + assert.False(t, Bool("F")) + assert.False(t, Bool("f")) + assert.True(t, Bool("true")) + assert.True(t, Bool("True")) + assert.True(t, Bool("t")) + assert.True(t, Bool("T")) + assert.True(t, Bool("1")) +}