Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding new functions bool and getenv #10

Merged
merged 1 commit into from
Feb 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
51 changes: 38 additions & 13 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package main

import (
"bufio"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"strconv"

"text/template"
)
Expand All @@ -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() {
Expand Down
40 changes: 40 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -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"))
}