Skip to content

Commit

Permalink
Refactor to support real bool values
Browse files Browse the repository at this point in the history
Signed-off-by: Mathieu Frenette <[email protected]>
  • Loading branch information
silphid committed Feb 15, 2021
1 parent 33ee888 commit 0d0eb1d
Show file tree
Hide file tree
Showing 17 changed files with 148 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"JEN_HOME": "${workspaceFolder}/dev/jenHome"
},
"args": [
"-v", "-y", "-t", "go-service", "-s", "PROJECT=acme100,PSQL=true,NEWRELIC=false", "do", "create"
"-v", "-y", "-t", "go-service", "-s", "PROJECT=acme100,PSQL=false,NEWRELIC=false", "do", "create"
],
"cwd": "${workspaceFolder}/dev/acme100"
}
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Code generator and script runner.

## Wishlist

- Fix boolean expressions
- either: Assume that "false" is true and "" is false (and adjust tests accordingly)
- or: Support boolean and numerical variable values
- `jen do` alone to prompt for action
- `jen export` to list env variables
- Reusable modules
Expand All @@ -16,7 +19,7 @@ Code generator and script runner.
```
placeholders:
projekt: {{.PROJECT | lower}}
PROJEKT: {{.PROJECT | upper}}
PROJEKT: {{.PROJECT | upper}},
```

- `--dry-run` flag (automatically turns on `--verbose`?)
Expand Down
12 changes: 8 additions & 4 deletions src/cmd/internal/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ type context struct {
// GetVars returns a dictionary of the project's variable names mapped to
// their corresponding values. It does not include the process' env var.
// Whenever you alter this map, you are responsible for later calling SaveProject().
func (c context) GetVars() map[string]string {
func (c context) GetVars() map[string]interface{} {
return c.project.Vars
}

Expand All @@ -93,10 +93,14 @@ func (c context) IsVarOverriden(name string) bool {
// project's name, which appears everywhere. For now, the only supported placeholder
// is PROJECT, but we will eventually make placeholders configurable in spec file.
func (c context) GetPlaceholders() map[string]string {
projectName, _ := c.project.Vars["PROJECT"]
value, _ := c.project.Vars["PROJECT"]
str, ok := value.(string)
if !ok {
return nil
}
return map[string]string{
"projekt": strings.ToLower(projectName),
"PROJEKT": strings.ToUpper(projectName),
"projekt": strings.ToLower(str),
"PROJEKT": strings.ToUpper(str),
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/internal/evaluation/evaluation.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type Context interface {
// GetVars returns a dictionary of the project's variable names mapped to
// their corresponding values. It does not include the process' env var.
// Do not alter this map, use SetVar() instead.
GetVars() map[string]string
GetVars() map[string]interface{}

// GetPlaceholders returns a map of special placeholders that can be used instead
// of go template expression, for lighter weight templating, especially for the
Expand Down
39 changes: 28 additions & 11 deletions src/internal/evaluation/evaluation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import (
"github.com/stretchr/testify/assert"
)

type varMap = map[string]interface{}
type strMap = map[string]string

type context struct {
vars strMap
vars varMap
placeholders strMap
}

func (c context) GetVars() map[string]string {
func (c context) GetVars() map[string]interface{} {
return c.vars
}

Expand All @@ -33,11 +34,11 @@ func (c context) GetShellVars() []string {

func TestEvalBoolExpression(t *testing.T) {
context := context{
vars: strMap{
vars: varMap{
"VAR1": "value1",
"VAR2": "value2",
"TRUE_VAR": "true",
"FALSE_VAR": "false",
"TRUE_VAR": true,
"FALSE_VAR": false,
"EMPTY_VAR": "",
},
}
Expand Down Expand Up @@ -76,7 +77,15 @@ func TestEvalBoolExpression(t *testing.T) {
Expected: true,
},
{
Condition: `eq .TRUE_VAR "true"`,
Condition: `eq .TRUE_VAR true`,
Expected: true,
},
{
Condition: `eq .FALSE_VAR false`,
Expected: true,
},
{
Condition: `.VAR1`,
Expected: true,
},
{
Expand All @@ -85,7 +94,7 @@ func TestEvalBoolExpression(t *testing.T) {
},
{
Condition: `.FALSE_VAR`,
Expected: true,
Expected: false,
},
{
Condition: `.EMPTY_VAR`,
Expand Down Expand Up @@ -118,10 +127,11 @@ func TestEvalBoolExpression(t *testing.T) {

func TestEvalFileName(t *testing.T) {
context := context{
vars: strMap{
vars: varMap{
"VAR1": "value1",
"VAR2": "value2",
"TRUE_VAR": "true",
"TRUE_VAR": true,
"FALSE_VAR": false,
"EMPTY_VAR": "",
},
placeholders: strMap{
Expand All @@ -143,6 +153,12 @@ func TestEvalFileName(t *testing.T) {
ExpectedInclude: true,
ExpectedRender: UnchangedRendering,
},
{
Name: `Name with false [[ .FALSE_VAR ]]conditional`,
ExpectedName: ``,
ExpectedInclude: false,
ExpectedRender: UnchangedRendering,
},
{
Name: `Name with false [[ .EMPTY_VAR ]]conditional`,
ExpectedName: ``,
Expand Down Expand Up @@ -200,10 +216,11 @@ func TestEvalFileName(t *testing.T) {

func TestEvalPromptValueTemplate(t *testing.T) {
context := context{
vars: strMap{
vars: varMap{
"VAR1": "value1",
"VAR2": "value2",
"TRUE_VAR": "true",
"TRUE_VAR": true,
"FALSE_VAR": false,
"EMPTY_VAR": "",
},
placeholders: strMap{
Expand Down
11 changes: 7 additions & 4 deletions src/internal/evaluation/getEntries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (

func TestGetEntries(t *testing.T) {
context := context{
vars: strMap{
vars: varMap{
"VAR1": "value1",
"VAR2": "value2",
"TRUE_VAR": "true",
"TRUE_VAR": true,
"FALSE_VAR": false,
"EMPTY_VAR": "",
},
placeholders: strMap{
Expand Down Expand Up @@ -102,7 +103,8 @@ func TestGetEntries(t *testing.T) {
Name: "conditional files",
Files: []string{
"dir1/file1[[.TRUE_VAR]].txt.tmpl",
"dir1/file2[[.UNDEFINED_VAR]].txt.tmpl",
"dir1/file2[[.FALSE_VAR]].txt.tmpl",
"dir1/file3[[.UNDEFINED_VAR]].txt.tmpl",
},
Expected: []entry{
{input: "dir1/file1[[.TRUE_VAR]].txt.tmpl", output: "dir1/file1.txt", render: true},
Expand All @@ -112,7 +114,8 @@ func TestGetEntries(t *testing.T) {
Name: "conditional dirs",
Files: []string{
"dir1[[.TRUE_VAR]]/file1.txt",
"dir2[[.UNDEFINED_VAR]]/file2.txt",
"dir2[[.FALSE_VAR]]/file2.txt",
"dir3[[.UNDEFINED_VAR]]/file3.txt",
},
Expected: []entry{
{input: "dir1[[.TRUE_VAR]]/file1.txt", output: "dir1/file1.txt"},
Expand Down
2 changes: 1 addition & 1 deletion src/internal/evaluation/renderFile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

func TestRenderFile(t *testing.T) {
context := context{
vars: strMap{
vars: varMap{
"VAR1": "value1",
"VAR2": "value2",
"TRUE_VAR": "true",
Expand Down
2 changes: 1 addition & 1 deletion src/internal/evaluation/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

func TestRender(t *testing.T) {
context := context{
vars: strMap{
vars: varMap{
"VAR1": "value1",
"VAR2": "value2",
"TRUE_VAR": "true",
Expand Down
2 changes: 1 addition & 1 deletion src/internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type Context interface {
// GetVars returns a dictionary of the project's variable names mapped to
// their corresponding values. It does not include the process' env var.
// Whenever you alter this map, you are responsible for later calling SaveProject().
GetVars() map[string]string
GetVars() map[string]interface{}

// SaveVars saves all of the project's variables to project file.
SaveProject() error
Expand Down
36 changes: 36 additions & 0 deletions src/internal/helpers/conversion/conversion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package conversion

import (
"fmt"
"strconv"
)

// ToString converts an abstract value (can be either string or bool) to its string representation
func ToString(value interface{}) (string, error) {
strValue, ok := value.(string)
if ok {
return strValue, nil
}

boolValue, ok := value.(bool)
if ok {
return strconv.FormatBool(boolValue), nil
}

return "", fmt.Errorf("failed to convert type into string: %t", value)
}

// ToBool converts an abstract value (can be either string or bool) to its string representation
func ToBool(value interface{}) (bool, error) {
boolValue, ok := value.(bool)
if ok {
return boolValue, nil
}

strValue, ok := value.(string)
if ok {
return strconv.ParseBool(strValue)
}

return false, fmt.Errorf("failed to convert type into bool: %t", value)
}
33 changes: 33 additions & 0 deletions src/internal/helpers/variables/variables.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package variables

import "github.com/Samasource/jen/src/internal/helpers/conversion"

// TryGetString tries to fetch given variable from map and return it as a string,
// also returning whether the variable was successfully found and converted.
func TryGetString(vars map[string]interface{}, name string) (string, bool) {
value, ok := vars[name]
if !ok {
return "", false
}

str, err := conversion.ToString(value)
if err != nil {
return "", false
}
return str, true
}

// TryGetBool tries to fetch given variable from map and return it as a bool,
// also returning whether the variable was successfully found and converted.
func TryGetBool(vars map[string]interface{}, name string) (bool, bool) {
value, ok := vars[name]
if !ok {
return false, false
}

b, err := conversion.ToBool(value)
if err != nil {
return false, false
}
return b, true
}
14 changes: 12 additions & 2 deletions src/internal/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func GetProjectDir() (string, error) {
type Project struct {
Version string
TemplateName string
Vars map[string]string
Vars map[string]interface{}
Dir string `yaml:"-"`
OverridenVars []string `yaml:"-"`
}
Expand Down Expand Up @@ -109,6 +109,9 @@ func LoadOrCreate(templateName string, skipConfirm bool, varOverrides []string)

if templateName != "" {
proj.TemplateName = templateName
if err := proj.Save(); err != nil {
return nil, err
}
}

templatesDir, err := home.GetTemplatesDir()
Expand All @@ -120,7 +123,9 @@ func LoadOrCreate(templateName string, skipConfirm bool, varOverrides []string)
if err != nil {
return nil, fmt.Errorf("prompting for template: %w", err)
}
proj.Save()
if err := proj.Save(); err != nil {
return nil, err
}
}

// Apply command-line variable overrides
Expand All @@ -134,6 +139,11 @@ func LoadOrCreate(templateName string, skipConfirm bool, varOverrides []string)
proj.Vars[name] = value
proj.OverridenVars = append(proj.OverridenVars, name)
}
if len(varOverrides) > 0 {
if err := proj.Save(); err != nil {
return nil, err
}
}

return proj, nil
}
Expand Down
7 changes: 4 additions & 3 deletions src/internal/project/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import (
"github.com/stretchr/testify/assert"
)

type varMap = map[string]interface{}
type strMap = map[string]string

func TestSaveAndLoad(t *testing.T) {
// Save
proj := Project{Vars: strMap{
"VAR1": "true",
"VAR2": "abc",
proj := Project{Vars: varMap{
"BOOL_VAR": true,
"STR_VAR": "abc",
}}
proj.Dir = getTempDir()
err := proj.Save()
Expand Down
2 changes: 1 addition & 1 deletion src/internal/spec/loadHelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func getString(node yaml.Node) (string, bool) {
return "", false
}
str := scalar.String()
// WORKAROUND: go-gypsy lib incorrectly loads `""` empty string as a literal of two double-quotes
// WORKAROUND: go-gypsy lib incorrectly loads double-quoted strings with quotes as part of the string itself
if strings.HasPrefix(str, `"`) && strings.HasSuffix(str, `"`) {
return str[1 : len(str)-1], true
}
Expand Down
3 changes: 2 additions & 1 deletion src/internal/steps/input/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/AlecAivazis/survey/v2"
"github.com/Samasource/jen/src/internal/evaluation"
"github.com/Samasource/jen/src/internal/exec"
"github.com/Samasource/jen/src/internal/helpers/variables"
)

// Prompt represents a single text input user prompt
Expand All @@ -28,7 +29,7 @@ func (p Prompt) Execute(context exec.Context) error {
vars := context.GetVars()

// Compute default value
defaultValue, ok := vars[p.Var]
defaultValue, ok := variables.TryGetString(vars, p.Var)
if !ok {
defaultValue, err = evaluation.EvalPromptValueTemplate(context, p.Default)
if err != nil {
Expand Down
Loading

0 comments on commit 0d0eb1d

Please sign in to comment.