Skip to content
Permalink
Browse files

Refactoring, simplifying logic

  • Loading branch information...
Depado committed Oct 24, 2018
1 parent 3cc013c commit 7648a3c8ea6d13af4d733187ebfeb67bce5d2f20
Showing with 167 additions and 21 deletions.
  1. +1 −1 conf/file.go
  2. +1 −1 conf/root.go
  3. +20 −19 conf/variables.go
  4. +145 −0 conf/variables_test.go
@@ -81,7 +81,7 @@ func (f *File) ParseFrontMatter() error {
return err
}
f.Metadata = &r
if f.Metadata.Variables != nil && len(f.Metadata.Variables.s) > 0 {
if f.Metadata.Variables != nil && len(*f.Metadata.Variables) > 0 {
utils.OkPrintln("Variables for single file", color.YellowString(f.Path))
f.Metadata.Variables.Prompt()
}
@@ -43,7 +43,7 @@ func (r Root) ExecuteCommands(dir string) {
for _, cmd := range r.After {
if cmd.Cmd != "" {
if cmd.If != "" && r.Variables != nil {
if v, ok := r.Variables.m[cmd.If]; ok && v.True() {
if v := r.Variables.FindNamed(cmd.If); v != nil && v.True() {
cmd.Run()
}
} else {
@@ -9,10 +9,18 @@ import (
yaml "gopkg.in/yaml.v2"
)

// Variables represents a map of variable
type Variables struct {
m map[string]*Variable
s []*Variable
// Variables is a slice of pointer to a single variable
type Variables []*Variable

// FindNamed will find a variable by name in the global variables. Returns nil
// if not found
func (vv Variables) FindNamed(s string) *Variable {
for _, v := range vv {
if v.Name == s {
return v
}
}
return nil
}

// FromMapSlice fills in the Variables struct with the data stored in a
@@ -21,20 +29,14 @@ func (vv *Variables) FromMapSlice(in yaml.MapSlice) {
for _, i := range in {
inv := &Variable{}
inv.FromMapItem(i)

k := i.Key.(string)
inv.Name = k
vv.m[k] = inv
vv.s = append(vv.s, inv)
*vv = append(*vv, inv)
}
}

// UnmarshalYAML defines a custom way to unmarshal to the Variables type.
// Specifically this allows to conserve the key order
func (vv *Variables) UnmarshalYAML(unmarshal func(interface{}) error) error {
variables := Variables{
m: make(map[string]*Variable),
}
var variables Variables
n := yaml.MapSlice{}
err := unmarshal(&n)
if err != nil {
@@ -47,15 +49,15 @@ func (vv *Variables) UnmarshalYAML(unmarshal func(interface{}) error) error {

// Prompt will prompt
func (vv Variables) Prompt() {
for _, v := range vv.s {
for _, v := range vv {
v.Prompt()
}
}

// Ctx generates the context from the variables
func (vv Variables) Ctx() map[string]interface{} {
ctx := make(map[string]interface{})
for _, v := range vv.s {
for _, v := range vv {
if v != nil {
if v.Confirm != nil {
ctx[v.Name] = *v.Confirm
@@ -101,8 +103,8 @@ type Variable struct {

// Confirm is used both for default variable and to store the result.
// If this field isn't nil, then a confirmation survey is used.
Confirm *bool `yaml:"confirm,omitempty"`
Variables *Variables `yaml:"variables,omitempty"`
Confirm *bool `yaml:"confirm,omitempty"`
Variables Variables `yaml:"variables,omitempty"`

Result string
Name string
@@ -111,6 +113,7 @@ type Variable struct {
// FromMapItem will fill the variable with the data stored in the input
// yaml.MapItem. Used to recursively parse nested variables.
func (v *Variable) FromMapItem(i yaml.MapItem) {
v.Name = i.Key.(string)
for _, data := range i.Value.(yaml.MapSlice) {
switch data.Key.(string) {
case "default":
@@ -129,9 +132,7 @@ func (v *Variable) FromMapItem(i yaml.MapItem) {
b := data.Value.(bool)
v.Confirm = &b
case "variables":
vv := &Variables{
m: make(map[string]*Variable),
}
var vv Variables
vv.FromMapSlice(data.Value.(yaml.MapSlice))
v.Variables = vv
}
@@ -0,0 +1,145 @@
package conf

import (
"reflect"
"testing"

"github.com/stretchr/testify/assert"
)

var (
tbool = true
empty = &Variable{Name: "empty"}
hasvalue = &Variable{Name: "value", Result: "result"}
hasbool = &Variable{Name: "bool", Confirm: &tbool}
subvar = &Variable{Name: "sub", Result: "ok"}
parentvar = &Variable{Name: "parent", Variables: Variables{subvar}}
)

func TestVariables_Ctx(t *testing.T) {
tests := []struct {
name string
fields Variables
want map[string]interface{}
}{
{"should get one", Variables{empty},
map[string]interface{}{empty.Name: ""}},
{"should get two vars", Variables{empty, hasvalue},
map[string]interface{}{empty.Name: "", hasvalue.Name: hasvalue.Result}},
{"should get bool", Variables{hasbool},
map[string]interface{}{hasbool.Name: true}},
{"should get all vars", Variables{empty, hasvalue, hasbool},
map[string]interface{}{empty.Name: "", hasvalue.Name: hasvalue.Result, hasbool.Name: true}},
{"should get even sub vars", Variables{parentvar},
map[string]interface{}{parentvar.Name: "", parentvar.Name + "_" + subvar.Name: "ok"}},
{"should get even all with sub vars", Variables{empty, hasvalue, hasbool, parentvar},
map[string]interface{}{empty.Name: "", hasvalue.Name: hasvalue.Result, hasbool.Name: true, parentvar.Name: "", parentvar.Name + "_" + subvar.Name: "ok"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.fields.Ctx(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Variables.Ctx() = %v, want %v", got, tt.want)
}
})
}
}

func TestVariables_AddToCtx(t *testing.T) {
basectx := map[string]interface{}{"one": "one", "two": true}
type args struct {
prefix string
ctx map[string]interface{}
}
tests := []struct {
name string
fields Variables
args args
expects map[string]interface{}
}{
{"should add empty to context", Variables{empty}, args{"", basectx},
map[string]interface{}{empty.Name: "", "one": "one", "two": true}},
{"should add boolean to context", Variables{hasbool}, args{"", basectx},
map[string]interface{}{hasbool.Name: true, "one": "one", "two": true}},
{"should add boolean with key to context", Variables{hasbool}, args{"sub", basectx},
map[string]interface{}{"sub_" + hasbool.Name: true, "one": "one", "two": true}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cp := make(map[string]interface{})
for k, v := range tt.args.ctx {
cp[k] = v
}
tt.fields.AddToCtx(tt.args.prefix, cp)
assert.Equal(t, tt.expects, cp)
})
}
}

func TestVariables_FindNamed(t *testing.T) {
vv := Variables{hasbool, hasvalue, empty}
type args struct {
s string
}
tests := []struct {
name string
vv Variables
args args
want *Variable
}{
{"should find even empty", vv, args{hasvalue.Name}, hasvalue},
{"should find bool", vv, args{hasbool.Name}, hasbool},
{"should find value", vv, args{hasvalue.Name}, hasvalue},
{"shouldn't find", vv, args{"random.jpg"}, nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.vv.FindNamed(tt.args.s)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Variables.FindNamed() got = %v, want %v", got, tt.want)
}
})
}
}

func TestVariable_True(t *testing.T) {
var fbool bool
type fields struct {
Default string
CustomPrompt string
Values []string
Help string
Required bool
Confirm *bool
Variables Variables
Result string
Name string
}
tests := []struct {
name string
fields fields
want bool
}{
{"should be false when confirm is false", fields{Confirm: &fbool}, false},
{"should be false when result is empty", fields{Result: ""}, false},
{"should be true when confirm is true", fields{Confirm: &tbool}, true},
{"should be true when result is true", fields{Result: "toto"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := &Variable{
Default: tt.fields.Default,
CustomPrompt: tt.fields.CustomPrompt,
Values: tt.fields.Values,
Help: tt.fields.Help,
Required: tt.fields.Required,
Confirm: tt.fields.Confirm,
Variables: tt.fields.Variables,
Result: tt.fields.Result,
Name: tt.fields.Name,
}
if got := v.True(); got != tt.want {
t.Errorf("Variable.True() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 7648a3c

Please sign in to comment.
You can’t perform that action at this time.