From 43856431325d7e65bd1c4038001d13f08703d19f Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sat, 15 Apr 2017 13:16:23 -0700 Subject: [PATCH 01/42] renamed Choice to Select --- README.md | 4 ++-- examples/simple.go | 4 ++-- choice.go => select.go | 38 ++++++++++++++++---------------- choice_test.go => select_test.go | 14 ++++++------ tests/select.go | 18 +++++++-------- 5 files changed, 39 insertions(+), 39 deletions(-) rename choice.go => select.go (81%) rename choice_test.go => select_test.go (85%) diff --git a/README.md b/README.md index 83c7010a..7367c6d6 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,9 @@ var qs = []*survey.Question{ }, { Name: "color", - Prompt: &survey.Choice{ + Prompt: &survey.Select{ Message: "Choose a color:", - Choices: []string{"red", "blue", "green"}, + Options: []string{"red", "blue", "green"}, Default: "red", }, }, diff --git a/examples/simple.go b/examples/simple.go index 6fd98b3d..d9db35e0 100644 --- a/examples/simple.go +++ b/examples/simple.go @@ -17,9 +17,9 @@ var simpleQs = []*survey.Question{ }, { Name: "color", - Prompt: &survey.Choice{ + Prompt: &survey.Select{ Message: "Choose a color:", - Choices: []string{"red", "blue", "green"}, + Options: []string{"red", "blue", "green"}, }, Validate: survey.Required, }, diff --git a/choice.go b/select.go similarity index 81% rename from choice.go rename to select.go index 2a5f2e96..0ef86fae 100644 --- a/choice.go +++ b/select.go @@ -9,29 +9,29 @@ import ( "github.com/chzyer/readline" ) -// Choice is a prompt that presents a list of various options to the user +// Select is a prompt that presents a list of various options to the user // for them to select using the arrow keys and enter. -type Choice struct { +type Select struct { Message string - Choices []string + Options []string Default string SelectedIndex int } // the data available to the templates when processing type SelectTemplateData struct { - Select Choice + Select Answer string } const ( SelectQuestionTemplate = ` {{- color "green+hb"}}? {{color "reset"}} -{{- color "default+hb"}}{{ $.Select.Message }} {{color "reset"}} +{{- color "default+hb"}}{{ .Message }} {{color "reset"}} {{- if .Answer}}{{color "cyan"}}{{.Answer}}{{color "reset"}}{{end}}` // the template used to show the list of Selects SelectChoicesTemplate = ` -{{- range $ix, $choice := $.Select.Choices}} +{{- range $ix, $choice := .Options}} {{- if eq $ix $.Select.SelectedIndex}}{{color "cyan+b"}}> {{else}}{{color "default+hb"}} {{end}} {{- $choice}} {{- color "reset"}} @@ -39,16 +39,16 @@ const ( ) // OnChange is called on every keypress. -func (s *Choice) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) { +func (s *Select) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) { // if the user pressed the enter key if key == terminal.KeyEnter { - return []rune(s.Choices[s.SelectedIndex]), 0, true + return []rune(s.Options[s.SelectedIndex]), 0, true // if the user pressed the up arrow } else if key == terminal.KeyArrowUp && s.SelectedIndex > 0 { // decrement the selected index s.SelectedIndex-- // if the user pressed down and there is room to move - } else if key == terminal.KeyArrowDown && s.SelectedIndex < len(s.Choices)-1 { + } else if key == terminal.KeyArrowDown && s.SelectedIndex < len(s.Options)-1 { // increment the selected index s.SelectedIndex++ } @@ -57,11 +57,11 @@ func (s *Choice) OnChange(line []rune, pos int, key rune) (newLine []rune, newPo s.render() // if we are not pressing ent - return []rune(s.Choices[s.SelectedIndex]), 0, true + return []rune(s.Options[s.SelectedIndex]), 0, true } -func (s *Choice) render() error { - for range s.Choices { +func (s *Select) render() error { + for range s.Options { terminal.CursorPreviousLine(1) terminal.EraseInLine(1) } @@ -81,7 +81,7 @@ func (s *Choice) render() error { return nil } -func (s *Choice) Prompt(rl *readline.Instance) (string, error) { +func (s *Select) Prompt(rl *readline.Instance) (string, error) { config := &readline.Config{ Listener: s, Stdout: ioutil.Discard, @@ -89,7 +89,7 @@ func (s *Choice) Prompt(rl *readline.Instance) (string, error) { rl.SetConfig(config) // if there are no options to render - if len(s.Choices) == 0 { + if len(s.Options) == 0 { // we failed return "", errors.New("please provide options to select from") } @@ -99,7 +99,7 @@ func (s *Choice) Prompt(rl *readline.Instance) (string, error) { // if there is a default if s.Default != "" { // find the choice - for i, opt := range s.Choices { + for i, opt := range s.Options { // if the option correponds to the default if opt == s.Default { // we found our initial value @@ -125,7 +125,7 @@ func (s *Choice) Prompt(rl *readline.Instance) (string, error) { terminal.CursorHide() // ask the question terminal.Println(out) - for range s.Choices { + for range s.Options { terminal.Println() } // start waiting for input @@ -141,7 +141,7 @@ func (s *Choice) Prompt(rl *readline.Instance) (string, error) { val = s.Default } else { // there is no default value so use the first - val = s.Choices[0] + val = s.Options[0] } } @@ -149,10 +149,10 @@ func (s *Choice) Prompt(rl *readline.Instance) (string, error) { return val, err } -func (s *Choice) Cleanup(rl *readline.Instance, val string) error { +func (s *Select) Cleanup(rl *readline.Instance, val string) error { terminal.CursorPreviousLine(1) terminal.EraseInLine(1) - for range s.Choices { + for range s.Options { terminal.CursorPreviousLine(1) terminal.EraseInLine(1) } diff --git a/choice_test.go b/select_test.go similarity index 85% rename from choice_test.go rename to select_test.go index d4478c91..2b1756b7 100644 --- a/choice_test.go +++ b/select_test.go @@ -11,8 +11,8 @@ func init() { func TestCanFormatSelectOptions(t *testing.T) { - prompt := &Choice{ - Choices: []string{"foo", "bar", "baz", "buz"}, + prompt := &Select{ + Options: []string{"foo", "bar", "baz", "buz"}, Default: "baz", } // TODO: figure out a way for the test to actually test this bit of code @@ -24,7 +24,7 @@ func TestCanFormatSelectOptions(t *testing.T) { ) if err != nil { - t.Errorf("Failed to run template to format choice choices: %s", err) + t.Errorf("Failed to run template to format choice options: %s", err) } expected := ` foo @@ -40,9 +40,9 @@ func TestCanFormatSelectOptions(t *testing.T) { func TestSelectFormatQuestion(t *testing.T) { - prompt := &Choice{ + prompt := &Select{ Message: "Pick your word:", - Choices: []string{"foo", "bar", "baz", "buz"}, + Options: []string{"foo", "bar", "baz", "buz"}, Default: "baz", } @@ -63,9 +63,9 @@ func TestSelectFormatQuestion(t *testing.T) { func TestSelectFormatAnswer(t *testing.T) { - prompt := &Choice{ + prompt := &Select{ Message: "Pick your word:", - Choices: []string{"foo", "bar", "baz", "buz"}, + Options: []string{"foo", "bar", "baz", "buz"}, Default: "baz", } diff --git a/tests/select.go b/tests/select.go index 6d62dd68..ba6e7c34 100644 --- a/tests/select.go +++ b/tests/select.go @@ -7,35 +7,35 @@ import ( var goodTable = []TestUtil.TestTableEntry{ { - "standard", &survey.Choice{ + "standard", &survey.Select{ Message: "Choose a color:", - Choices: []string{"red", "blue", "green"}, + Options: []string{"red", "blue", "green"}, }, }, { - "short", &survey.Choice{ + "short", &survey.Select{ Message: "Choose a color:", - Choices: []string{"red", "blue"}, + Options: []string{"red", "blue"}, }, }, { - "default", &survey.Choice{ + "default", &survey.Select{ Message: "Choose a color (should default blue):", - Choices: []string{"red", "blue", "green"}, + Options: []string{"red", "blue", "green"}, Default: "blue", }, }, { - "one", &survey.Choice{ + "one", &survey.Select{ Message: "Choose one:", - Choices: []string{"hello"}, + Options: []string{"hello"}, }, }, } var badTable = []TestUtil.TestTableEntry{ { - "no Choices", &survey.Choice{ + "no options", &survey.Select{ Message: "Choose one:", }, }, From 4c517593646d3a410c8edbb70965300352824ebb Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sat, 15 Apr 2017 13:17:03 -0700 Subject: [PATCH 02/42] fixed version link in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7367c6d6..d0332de6 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ package main import ( "fmt" - "gopkg.in/alecaivazis/survey.v0" + "gopkg.in/alecaivazis/survey.v1" ) // the questions to ask From 7902ea32b18b1f48cb68f66fb60762adff84b3f0 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sat, 15 Apr 2017 13:22:44 -0700 Subject: [PATCH 03/42] added core package --- .travis.yml | 4 ++-- confirm.go | 7 ++++--- confirm_test.go | 10 ++++++---- template.go => core/template.go | 2 +- input.go | 5 +++-- input_test.go | 8 +++++--- multichoice.go | 7 ++++--- multichoice_test.go | 14 +++++++++----- password.go | 4 +++- password_test.go | 6 ++++-- select.go | 7 ++++--- select_test.go | 10 ++++++---- survey.go | 3 ++- survey_test.go | 6 ++++-- 14 files changed, 57 insertions(+), 36 deletions(-) rename template.go => core/template.go (98%) diff --git a/.travis.yml b/.travis.yml index e04b93ea..a51042e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go install: - - go get -t . ./terminal/... + - go get -t . ./terminal/... ./core/... script: - - go test -v . ./terminal/... + - go test -v . ./terminal/... ./core/... diff --git a/confirm.go b/confirm.go index 9425f842..1a5c6de6 100644 --- a/confirm.go +++ b/confirm.go @@ -4,6 +4,7 @@ import ( "fmt" "regexp" + "github.com/alecaivazis/survey/core" "github.com/alecaivazis/survey/terminal" "github.com/chzyer/readline" ) @@ -64,7 +65,7 @@ func (c *Confirm) getBool(rl *readline.Instance) (bool, error) { answer = c.Default default: // we didnt get a valid answer, so print error and prompt again - out, err := RunTemplate( + out, err := core.RunTemplate( ErrorTemplate, fmt.Errorf("%q is not a valid answer, please try again.", val), ) // if something went wrong @@ -97,7 +98,7 @@ func (c *Confirm) Prompt(rl *readline.Instance) (string, error) { } // render the question template - out, err := RunTemplate( + out, err := core.RunTemplate( ConfirmQuestionTemplate, ConfirmTemplateData{Confirm: *c}, ) @@ -131,7 +132,7 @@ func (c *Confirm) Cleanup(rl *readline.Instance, val string) error { terminal.EraseInLine(1) // render the template - out, err := RunTemplate( + out, err := core.RunTemplate( ConfirmQuestionTemplate, ConfirmTemplateData{Confirm: *c, Answer: val}, ) diff --git a/confirm_test.go b/confirm_test.go index ad730fb9..feaf15f3 100644 --- a/confirm_test.go +++ b/confirm_test.go @@ -2,11 +2,13 @@ package survey import ( "testing" + + "github.com/alecaivazis/survey/core" ) func init() { // disable color output for all prompts to simplify testing - DisableColor = true + core.DisableColor = true } func TestConfirmFormatQuestion(t *testing.T) { @@ -16,7 +18,7 @@ func TestConfirmFormatQuestion(t *testing.T) { Default: true, } - actual, err := RunTemplate( + actual, err := core.RunTemplate( ConfirmQuestionTemplate, ConfirmTemplateData{Confirm: *prompt}, ) @@ -38,7 +40,7 @@ func TestConfirmFormatQuestionDefaultFalse(t *testing.T) { Default: false, } - actual, err := RunTemplate( + actual, err := core.RunTemplate( ConfirmQuestionTemplate, ConfirmTemplateData{Confirm: *prompt}, ) @@ -60,7 +62,7 @@ func TestConfirmFormatAnswer(t *testing.T) { Message: "Is pizza your favorite food?", } - actual, err := RunTemplate( + actual, err := core.RunTemplate( ConfirmQuestionTemplate, ConfirmTemplateData{Confirm: *prompt, Answer: "Yes"}, ) diff --git a/template.go b/core/template.go similarity index 98% rename from template.go rename to core/template.go index 88ce8a2f..c2716cc0 100644 --- a/template.go +++ b/core/template.go @@ -1,4 +1,4 @@ -package survey +package core import ( "bytes" diff --git a/input.go b/input.go index fcf0cbf0..872fc90f 100644 --- a/input.go +++ b/input.go @@ -3,6 +3,7 @@ package survey import ( "fmt" + "github.com/alecaivazis/survey/core" "github.com/alecaivazis/survey/terminal" "github.com/chzyer/readline" ) @@ -32,7 +33,7 @@ var InputQuestionTemplate = ` func (i *Input) Prompt(rl *readline.Instance) (line string, err error) { // render the template - out, err := RunTemplate( + out, err := core.RunTemplate( InputQuestionTemplate, InputTemplateData{Input: *i}, ) @@ -54,7 +55,7 @@ func (i *Input) Cleanup(rl *readline.Instance, val string) error { terminal.EraseInLine(1) // render the template - out, err := RunTemplate( + out, err := core.RunTemplate( InputQuestionTemplate, InputTemplateData{Input: *i, Answer: val}, ) diff --git a/input_test.go b/input_test.go index 3d680a6a..5b5d3edf 100644 --- a/input_test.go +++ b/input_test.go @@ -2,11 +2,13 @@ package survey import ( "testing" + + "github.com/alecaivazis/survey/core" ) func init() { // disable color output for all prompts to simplify testing - DisableColor = true + core.DisableColor = true } func TestInputFormatQuestion(t *testing.T) { @@ -16,7 +18,7 @@ func TestInputFormatQuestion(t *testing.T) { Default: "April", } - actual, err := RunTemplate( + actual, err := core.RunTemplate( InputQuestionTemplate, InputTemplateData{Input: *prompt}, ) @@ -38,7 +40,7 @@ func TestInputFormatAnswer(t *testing.T) { Default: "April", } - actual, err := RunTemplate( + actual, err := core.RunTemplate( InputQuestionTemplate, InputTemplateData{Input: *prompt, Answer: "October"}, ) diff --git a/multichoice.go b/multichoice.go index 3310baf9..19ba8234 100644 --- a/multichoice.go +++ b/multichoice.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "strings" + "github.com/alecaivazis/survey/core" "github.com/alecaivazis/survey/terminal" "github.com/chzyer/readline" ) @@ -80,7 +81,7 @@ func (m *MultiChoice) render() error { } // render the template summarizing the current state - out, err := RunTemplate( + out, err := core.RunTemplate( MultiChoiceOptionsTemplate, MultiChoiceTemplateData{ MultiChoice: *m, @@ -137,7 +138,7 @@ func (m *MultiChoice) Prompt(rl *readline.Instance) (string, error) { return "", errors.New("please provide options to select from") } // generate the template for the current state of the prompt - out, err := RunTemplate( + out, err := core.RunTemplate( MultiChoiceQuestionTemplate, MultiChoiceTemplateData{ MultiChoice: *m, @@ -206,7 +207,7 @@ func (m *MultiChoice) Cleanup(rl *readline.Instance, val string) error { json.Unmarshal([]byte(val), &value) // execute the output summary template with the answer - output, err := RunTemplate( + output, err := core.RunTemplate( MultiChoiceQuestionTemplate, MultiChoiceTemplateData{ MultiChoice: *m, diff --git a/multichoice_test.go b/multichoice_test.go index 41c38d9c..79486752 100644 --- a/multichoice_test.go +++ b/multichoice_test.go @@ -1,10 +1,14 @@ package survey -import "testing" +import ( + "testing" + + "github.com/alecaivazis/survey/core" +) func init() { // disable color output for all prompts to simplify testing - DisableColor = true + core.DisableColor = true } func TestCanFormatMultiChoiceOptions(t *testing.T) { @@ -14,7 +18,7 @@ func TestCanFormatMultiChoiceOptions(t *testing.T) { Defaults: []string{"bar", "buz"}, } - actual, err := RunTemplate( + actual, err := core.RunTemplate( MultiChoiceOptionsTemplate, MultiChoiceTemplateData{ MultiChoice: *prompt, @@ -46,7 +50,7 @@ func TestMultiChoiceFormatQuestion(t *testing.T) { Defaults: []string{"bar", "buz"}, } - actual, err := RunTemplate( + actual, err := core.RunTemplate( MultiChoiceQuestionTemplate, MultiChoiceTemplateData{MultiChoice: *prompt}, ) @@ -69,7 +73,7 @@ func TestMultiChoiceFormatAnswer(t *testing.T) { Defaults: []string{"bar", "buz"}, } - actual, err := RunTemplate( + actual, err := core.RunTemplate( MultiChoiceQuestionTemplate, MultiChoiceTemplateData{MultiChoice: *prompt, Answer: []string{"foo", "buz"}}, ) diff --git a/password.go b/password.go index 1c9f26c0..b4d95a65 100644 --- a/password.go +++ b/password.go @@ -2,6 +2,8 @@ package survey import ( "github.com/chzyer/readline" + + "github.com/alecaivazis/survey/core" ) // Password is like a normal Input but the text shows up as *'s and @@ -17,7 +19,7 @@ var PasswordQuestionTemplate = ` func (p *Password) Prompt(rl *readline.Instance) (line string, err error) { // render the question template - out, err := RunTemplate( + out, err := core.RunTemplate( PasswordQuestionTemplate, *p, ) diff --git a/password_test.go b/password_test.go index 3fd7b7c3..6a871048 100644 --- a/password_test.go +++ b/password_test.go @@ -2,11 +2,13 @@ package survey import ( "testing" + + "github.com/alecaivazis/survey/core" ) func init() { // disable color output for all prompts to simplify testing - DisableColor = true + core.DisableColor = true } func TestPasswordFormatQuestion(t *testing.T) { @@ -15,7 +17,7 @@ func TestPasswordFormatQuestion(t *testing.T) { Message: "Tell me your secret:", } - actual, err := RunTemplate( + actual, err := core.RunTemplate( PasswordQuestionTemplate, *prompt, ) diff --git a/select.go b/select.go index 0ef86fae..2268d774 100644 --- a/select.go +++ b/select.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "strings" + "github.com/alecaivazis/survey/core" "github.com/alecaivazis/survey/terminal" "github.com/chzyer/readline" ) @@ -67,7 +68,7 @@ func (s *Select) render() error { } // the formatted response - out, err := RunTemplate( + out, err := core.RunTemplate( SelectChoicesTemplate, SelectTemplateData{Select: *s}, ) @@ -113,7 +114,7 @@ func (s *Select) Prompt(rl *readline.Instance) (string, error) { s.SelectedIndex = sel // render the initial question - out, err := RunTemplate( + out, err := core.RunTemplate( SelectQuestionTemplate, SelectTemplateData{Select: *s}, ) @@ -158,7 +159,7 @@ func (s *Select) Cleanup(rl *readline.Instance, val string) error { } // execute the output summary template with the answer - output, err := RunTemplate( + output, err := core.RunTemplate( SelectQuestionTemplate, SelectTemplateData{Select: *s, Answer: val}, ) diff --git a/select_test.go b/select_test.go index 2b1756b7..c6b83f6b 100644 --- a/select_test.go +++ b/select_test.go @@ -2,11 +2,13 @@ package survey import ( "testing" + + "github.com/alecaivazis/survey/core" ) func init() { // disable color output for all prompts to simplify testing - DisableColor = true + core.DisableColor = true } func TestCanFormatSelectOptions(t *testing.T) { @@ -18,7 +20,7 @@ func TestCanFormatSelectOptions(t *testing.T) { // TODO: figure out a way for the test to actually test this bit of code prompt.SelectedIndex = 2 - actual, err := RunTemplate( + actual, err := core.RunTemplate( SelectChoicesTemplate, SelectTemplateData{Select: *prompt}, ) @@ -46,7 +48,7 @@ func TestSelectFormatQuestion(t *testing.T) { Default: "baz", } - actual, err := RunTemplate( + actual, err := core.RunTemplate( SelectQuestionTemplate, SelectTemplateData{Select: *prompt}, ) @@ -69,7 +71,7 @@ func TestSelectFormatAnswer(t *testing.T) { Default: "baz", } - actual, err := RunTemplate( + actual, err := core.RunTemplate( SelectQuestionTemplate, SelectTemplateData{Select: *prompt, Answer: "buz"}, ) diff --git a/survey.go b/survey.go index d1c20d90..f2c0c03e 100644 --- a/survey.go +++ b/survey.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/alecaivazis/survey/core" "github.com/alecaivazis/survey/terminal" "github.com/chzyer/readline" ) @@ -73,7 +74,7 @@ func Ask(qs []*Question) (map[string]string, error) { if q.Validate != nil { // wait for a valid response for invalid := q.Validate(ans); invalid != nil; invalid = q.Validate(ans) { - out, err := RunTemplate(ErrorTemplate, invalid) + out, err := core.RunTemplate(ErrorTemplate, invalid) if err != nil { return nil, err } diff --git a/survey_test.go b/survey_test.go index dc8219d8..e73765aa 100644 --- a/survey_test.go +++ b/survey_test.go @@ -3,18 +3,20 @@ package survey import ( "fmt" "testing" + + "github.com/alecaivazis/survey/core" ) func init() { // disable color output for all prompts to simplify testing - DisableColor = true + core.DisableColor = true } func TestValidationError(t *testing.T) { err := fmt.Errorf("Football is not a valid month") - actual, err := RunTemplate( + actual, err := core.RunTemplate( ErrorTemplate, err, ) From b3fb032c76b87c0634e28702986745ab56d661bf Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sat, 15 Apr 2017 14:12:36 -0700 Subject: [PATCH 04/42] added task file --- .tasks.yml | 10 ++++++++++ .travis.yml | 7 +++++-- core/write.go | 1 + core/write_test.go | 1 + tests/ask.go | 5 +++-- 5 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 .tasks.yml create mode 100644 core/write.go create mode 100644 core/write_test.go diff --git a/.tasks.yml b/.tasks.yml new file mode 100644 index 00000000..6220b567 --- /dev/null +++ b/.tasks.yml @@ -0,0 +1,10 @@ +tests: + summary: Run the test suite + command: go test -v {{.files}} + +install-deps: + summary: Install all of package dependencies + command: go get -t {{.files}} + +variables: + files: '$(go list -v ./... | grep -Ev "github.com/alecaivazis/survey/(tests|examples)")' diff --git a/.travis.yml b/.travis.yml index a51042e1..114ee0c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,10 @@ language: go +before_install: + - go get github.com/alecaivazis/run + install: - - go get -t . ./terminal/... ./core/... + - run install-deps script: - - go test -v . ./terminal/... ./core/... + - run tests diff --git a/core/write.go b/core/write.go new file mode 100644 index 00000000..9a8bc959 --- /dev/null +++ b/core/write.go @@ -0,0 +1 @@ +package core diff --git a/core/write_test.go b/core/write_test.go new file mode 100644 index 00000000..9a8bc959 --- /dev/null +++ b/core/write_test.go @@ -0,0 +1 @@ +package core diff --git a/tests/ask.go b/tests/ask.go index af179cb2..bbf71731 100644 --- a/tests/ask.go +++ b/tests/ask.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/alecaivazis/survey" ) @@ -16,9 +17,9 @@ var simpleQs = []*survey.Question{ }, { Name: "color", - Prompt: &survey.Choice{ + Prompt: &survey.Select{ Message: "Choose a color:", - Choices: []string{"red", "blue", "green", "yellow"}, + Options: []string{"red", "blue", "green", "yellow"}, Default: "yellow", }, Validate: survey.Required, From 27742687b656e6765667e6caf9679732b72c5da9 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sat, 15 Apr 2017 23:42:33 -0700 Subject: [PATCH 05/42] added basic panic handling to write --- .tasks.yml | 2 +- core/write.go | 32 ++++++++++++++++++++++++++++++++ core/write_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/.tasks.yml b/.tasks.yml index 6220b567..1dd5ea20 100644 --- a/.tasks.yml +++ b/.tasks.yml @@ -1,6 +1,6 @@ tests: summary: Run the test suite - command: go test -v {{.files}} + command: go test {{.files}} install-deps: summary: Install all of package dependencies diff --git a/core/write.go b/core/write.go index 9a8bc959..8e69abfc 100644 --- a/core/write.go +++ b/core/write.go @@ -1 +1,33 @@ package core + +import ( + "errors" + "reflect" +) + +// Write takes a value and copies it to the target +func Write(t interface{}, v interface{}) (err error) { + // when we're done + defer func() { + // if there was a panic + if r := recover(); r != nil { + // pass the panic on as an error + err = r.(error) + } + }() + + // the target to write to + target := reflect.ValueOf(t) + value := reflect.ValueOf(v) + + // make sure we were handed a point + if target.Kind() != reflect.Ptr { + return errors.New("you must pass a pointer as the target of a Write operation") + } + + // set the value of the object we're pointing to to match the value + target.Elem().SetBool(value.Bool()) + + // nothing went wrong + return nil +} diff --git a/core/write_test.go b/core/write_test.go index 9a8bc959..aea9bb7e 100644 --- a/core/write_test.go +++ b/core/write_test.go @@ -1 +1,42 @@ package core + +import ( + "testing" +) + +func TestWrite_returnsErrorIfTargetNotPtr(t *testing.T) { + // try to copy a value to a non-pointer + err := Write(true, true) + // make sure there was an error + if err == nil { + t.Error("Did not encounter error when writing to non-pointer.") + } +} + +func TestWrite_canWriteToBool(t *testing.T) { + // a pointer to hold the boolean value + ptr := true + + // try to copy a false value to the pointer + Write(&ptr, false) + + // if the value is try + if ptr { + // the test failed + t.Error("Could not write a false bool to a pointer") + } +} + +func TestWrite_recoversInvalidReflection(t *testing.T) { + // a variable to mutate + ptr := false + + // write a boolean value to the string + err := Write(&ptr, "hello") + + // if there was no error + if err == nil { + // the test failed + t.Error("Did not encounter error when forced invalid write.") + } +} From 3b08888d7b8124f354a430e473f1cd0f14cc81b0 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 00:16:36 -0700 Subject: [PATCH 06/42] can write boolean and string --- core/write.go | 53 +++++++++++++++++++++++++++++++++++----------- core/write_test.go | 42 ++++++++++++++++++++++++++++++------ 2 files changed, 76 insertions(+), 19 deletions(-) diff --git a/core/write.go b/core/write.go index 8e69abfc..6b1f7ce2 100644 --- a/core/write.go +++ b/core/write.go @@ -7,15 +7,6 @@ import ( // Write takes a value and copies it to the target func Write(t interface{}, v interface{}) (err error) { - // when we're done - defer func() { - // if there was a panic - if r := recover(); r != nil { - // pass the panic on as an error - err = r.(error) - } - }() - // the target to write to target := reflect.ValueOf(t) value := reflect.ValueOf(v) @@ -25,9 +16,47 @@ func Write(t interface{}, v interface{}) (err error) { return errors.New("you must pass a pointer as the target of a Write operation") } - // set the value of the object we're pointing to to match the value - target.Elem().SetBool(value.Bool()) + // handle the target based on its prop + switch target.Elem().Kind() { + // if we are writing to a string + case reflect.String: + err = writeString(target.Elem(), value) + // if we are writing to a bool + case reflect.Bool: + err = writeBool(target.Elem(), value) + } + + // we're done + return err +} + +func writeBool(target, source reflect.Value) (err error) { + // make sure we handle the source type + switch source.Kind() { + // if we are turning a boolean into a boolean + case reflect.Bool: + // just copy the boolean over + target.SetBool(source.Bool()) + // otherwise its a source we do not recognize + default: + err = errors.New("Cannot convert to bool") + } + // nothing went wrong + return err +} + +func writeString(target, source reflect.Value) (err error) { + // make sure we handle the source type + switch source.Kind() { + // if we are turning a string into a string + case reflect.String: + // just copy the string over + target.SetString(source.String()) + // otherwise its a source we do not recognize + default: + err = errors.New("Cannot convert to string") + } // nothing went wrong - return nil + return err } diff --git a/core/write_test.go b/core/write_test.go index aea9bb7e..329c3c67 100644 --- a/core/write_test.go +++ b/core/write_test.go @@ -20,23 +20,51 @@ func TestWrite_canWriteToBool(t *testing.T) { // try to copy a false value to the pointer Write(&ptr, false) - // if the value is try + // if the value is true if ptr { // the test failed t.Error("Could not write a false bool to a pointer") } } -func TestWrite_recoversInvalidReflection(t *testing.T) { - // a variable to mutate - ptr := false +func TestWrite_canWriteString(t *testing.T) { + // a pointer to hold the boolean value + ptr := "" - // write a boolean value to the string + // try to copy a false value to the pointer err := Write(&ptr, "hello") + if err != nil { + t.Error(err) + } + + // if the value is not what we wrote + if ptr != "hello" { + t.Error("Could not write a string value to a pointer") + } +} - // if there was no error +func TestWrite_gracefullyHandlesFailedStringWrites(t *testing.T) { + // a pointer to hold the boolean value + ptr := "" + // try to copy a false value to the pointer + err := Write(&ptr, false) + // if the value is try if err == nil { // the test failed - t.Error("Did not encounter error when forced invalid write.") + t.Error("Did not encouner error when casting boolean to string") } } + +// func TestWrite_recoversInvalidReflection(t *testing.T) { +// // a variable to mutate +// ptr := false + +// // write a boolean value to the string +// err := Write(&ptr, "hello") +// fmt.Println(err.Error()) +// // if there was no error +// if err == nil { +// // the test failed +// t.Error("Did not encounter error when forced invalid write.") +// } +// } From aaa1e55042b335fb856b79c2c48cbe1cfa746c1f Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 11:42:03 -0700 Subject: [PATCH 07/42] added utility for writing to specific fields in an object --- core/write.go | 95 +++++++++++++++++++------------- core/write_test.go | 134 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 169 insertions(+), 60 deletions(-) diff --git a/core/write.go b/core/write.go index 6b1f7ce2..f4361f04 100644 --- a/core/write.go +++ b/core/write.go @@ -2,61 +2,80 @@ package core import ( "errors" + "fmt" "reflect" + "strings" ) -// Write takes a value and copies it to the target -func Write(t interface{}, v interface{}) (err error) { +func WriteAnswer(t interface{}, name string, v interface{}) (err error) { // the target to write to target := reflect.ValueOf(t) + // the value to write from value := reflect.ValueOf(v) - // make sure we were handed a point + // make sure we are writing to a pointer if target.Kind() != reflect.Ptr { return errors.New("you must pass a pointer as the target of a Write operation") } + // the object "inside" of the target pointer + elem := target.Elem() + + // handle the special types + switch elem.Kind() { + // if we are writing to a struct + case reflect.Struct: + // get the name of the field that matches the string we were given + fieldIndex, err := findFieldName(elem, name) + // if something went wrong + if err != nil { + // bubble up + return err + } - // handle the target based on its prop - switch target.Elem().Kind() { - // if we are writing to a string - case reflect.String: - err = writeString(target.Elem(), value) - // if we are writing to a bool - case reflect.Bool: - err = writeBool(target.Elem(), value) + // copy the value over to the field + return copy(elem.Field(fieldIndex), value) } - // we're done - return err + // otherwise just copy the value to the target + return copy(elem, value) } -func writeBool(target, source reflect.Value) (err error) { - // make sure we handle the source type - switch source.Kind() { - // if we are turning a boolean into a boolean - case reflect.Bool: - // just copy the boolean over - target.SetBool(source.Bool()) - // otherwise its a source we do not recognize - default: - err = errors.New("Cannot convert to bool") +func findFieldName(s reflect.Value, name string) (int, error) { + // the type of the value + sType := s.Type() + // scan the fields of the struct + for i := 0; i < sType.NumField(); i++ { + // the field we are current scanning + field := sType.Field(i) + + // if the name of the field matches what we're looking for + if strings.ToLower(field.Name) == name { + return i, nil + } } - // nothing went wrong - return err + // we didn't find the field + return -1, fmt.Errorf("could not find field matching %v", name) } -func writeString(target, source reflect.Value) (err error) { - // make sure we handle the source type - switch source.Kind() { - // if we are turning a string into a string - case reflect.String: - // just copy the string over - target.SetString(source.String()) - // otherwise its a source we do not recognize - default: - err = errors.New("Cannot convert to string") - } +// Write takes a value and copies it to the target +func copy(t reflect.Value, v reflect.Value) (err error) { + // if something ends up panicing we need to catch it in a deferred func + defer func() { + if r := recover(); r != nil { + // if we paniced with an error + if _, ok := r.(error); ok { + // cast the result to an error object + err = r.(error) + } else if _, ok := r.(string); ok { + // otherwise we could have paniced with a string so wrap it in an error + err = errors.New(r.(string)) + } + } + }() - // nothing went wrong - return err + // attempt to copy the underlying value to the target + t.Set(v) + + // we're done + return } diff --git a/core/write_test.go b/core/write_test.go index 329c3c67..d040e34a 100644 --- a/core/write_test.go +++ b/core/write_test.go @@ -1,12 +1,14 @@ package core import ( + "fmt" + "reflect" "testing" ) func TestWrite_returnsErrorIfTargetNotPtr(t *testing.T) { // try to copy a value to a non-pointer - err := Write(true, true) + err := WriteAnswer(true, "hello", true) // make sure there was an error if err == nil { t.Error("Did not encounter error when writing to non-pointer.") @@ -18,7 +20,7 @@ func TestWrite_canWriteToBool(t *testing.T) { ptr := true // try to copy a false value to the pointer - Write(&ptr, false) + WriteAnswer(&ptr, "", false) // if the value is true if ptr { @@ -32,7 +34,7 @@ func TestWrite_canWriteString(t *testing.T) { ptr := "" // try to copy a false value to the pointer - err := Write(&ptr, "hello") + err := WriteAnswer(&ptr, "", "hello") if err != nil { t.Error(err) } @@ -43,28 +45,116 @@ func TestWrite_canWriteString(t *testing.T) { } } -func TestWrite_gracefullyHandlesFailedStringWrites(t *testing.T) { - // a pointer to hold the boolean value +func TestWrite_canWriteSlice(t *testing.T) { + // a pointer to hold the value + ptr := []string{} + + // copy in a value + WriteAnswer(&ptr, "", []string{"hello", "world"}) + + // make sure there are two entries + if len(ptr) != 2 { + // the test failed + t.Errorf("Incorrect number of entries in written list. Expected 2, found %v.", len(ptr)) + // dont move on + return + } + + // make sure the first entry is hello + if ptr[0] != "hello" { + // the test failed + t.Errorf("incorrect first value in written pointer. expected hello found %v.", ptr[0]) + } + + // make sure the second entry is world + if ptr[1] != "world" { + // the test failed + t.Errorf("incorrect second value in written pointer. expected world found %v.", ptr[0]) + } +} + +func TestWrite_recoversInvalidReflection(t *testing.T) { + // a variable to mutate + ptr := false + + // write a boolean value to the string + err := WriteAnswer(&ptr, "", "hello") + + // if there was no error + if err == nil { + // the test failed + t.Error("Did not encounter error when forced invalid write.") + } +} + +func TestWriteAnswer_handlesNonStructValues(t *testing.T) { + // the value to write to ptr := "" - // try to copy a false value to the pointer - err := Write(&ptr, false) - // if the value is try + + // write a value to the pointer + WriteAnswer(&ptr, "", "world") + + // if we didn't change the value appropriate + if ptr != "world" { + // the test failed + t.Error("Did not write value to primitive pointer") + } +} + +func TestWriteAnswer_canMutateStruct(t *testing.T) { + // the struct to hold the answer + ptr := struct{ Name string }{} + + // write a value to an existing field + err := WriteAnswer(&ptr, "name", "world") + if err != nil { + // the test failed + t.Errorf("Encountered error while writing answer: %v", err.Error()) + // we're done here + return + } + + fmt.Println(ptr.Name) + + // make sure we changed the field + if ptr.Name != "world" { + // the test failed + t.Error("Did not mutate struct field when writing answer.") + } +} + +func TestWriteAnswer_returnsErrWhenFieldNotFound(t *testing.T) { + // the struct to hold the answer + ptr := struct{ Name string }{} + + // write a value to an existing field + err := WriteAnswer(&ptr, "", "world") + if err == nil { // the test failed - t.Error("Did not encouner error when casting boolean to string") + t.Error("Did not encountered error while writing answer to non-existing field.") } } -// func TestWrite_recoversInvalidReflection(t *testing.T) { -// // a variable to mutate -// ptr := false - -// // write a boolean value to the string -// err := Write(&ptr, "hello") -// fmt.Println(err.Error()) -// // if there was no error -// if err == nil { -// // the test failed -// t.Error("Did not encounter error when forced invalid write.") -// } -// } +func TestFindFieldName_canFindExportedField(t *testing.T) { + // the struct to look through + ptr := struct{ Name string }{} + + // create a reflective wrapper over the value + val := reflect.ValueOf(ptr) + + // find the field matching "name" + field, err := findFieldName(val, "name") + // if something went wrong + if err != nil { + // the test failed + t.Error(err.Error()) + return + } + + // make sure we got the right value + if field != 0 { + // the test failed + t.Errorf("Did not find the correct field name. Expected 'Name' found %v.", field) + } +} From 424ecdd5a579d6023142b4ceaf31dbeee5369d8b Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 11:47:00 -0700 Subject: [PATCH 08/42] changed name of field search to better reflect return value --- core/write.go | 4 ++-- core/write_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/write.go b/core/write.go index f4361f04..b8085067 100644 --- a/core/write.go +++ b/core/write.go @@ -25,7 +25,7 @@ func WriteAnswer(t interface{}, name string, v interface{}) (err error) { // if we are writing to a struct case reflect.Struct: // get the name of the field that matches the string we were given - fieldIndex, err := findFieldName(elem, name) + fieldIndex, err := findFieldIndex(elem, name) // if something went wrong if err != nil { // bubble up @@ -40,7 +40,7 @@ func WriteAnswer(t interface{}, name string, v interface{}) (err error) { return copy(elem, value) } -func findFieldName(s reflect.Value, name string) (int, error) { +func findFieldIndex(s reflect.Value, name string) (int, error) { // the type of the value sType := s.Type() // scan the fields of the struct diff --git a/core/write_test.go b/core/write_test.go index d040e34a..288f9d1d 100644 --- a/core/write_test.go +++ b/core/write_test.go @@ -136,7 +136,7 @@ func TestWriteAnswer_returnsErrWhenFieldNotFound(t *testing.T) { } } -func TestFindFieldName_canFindExportedField(t *testing.T) { +func TestFindFieldIndex_canFindExportedField(t *testing.T) { // the struct to look through ptr := struct{ Name string }{} @@ -144,7 +144,7 @@ func TestFindFieldName_canFindExportedField(t *testing.T) { val := reflect.ValueOf(ptr) // find the field matching "name" - field, err := findFieldName(val, "name") + fieldIndex, err := findFieldIndex(val, "name") // if something went wrong if err != nil { // the test failed @@ -153,8 +153,8 @@ func TestFindFieldName_canFindExportedField(t *testing.T) { } // make sure we got the right value - if field != 0 { + if val.Type().Field(fieldIndex).Name != "Name" { // the test failed - t.Errorf("Did not find the correct field name. Expected 'Name' found %v.", field) + t.Errorf("Did not find the correct field name. Expected 'Name' found %v.", val.Type().Field(fieldIndex).Name) } } From 6851bfa266c8d645b9947e96364e62cd13ece457 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 12:05:18 -0700 Subject: [PATCH 09/42] findFieldIndex can now locate tagged fields --- core/write.go | 11 +++++++++++ core/write_test.go | 29 ++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/core/write.go b/core/write.go index b8085067..dd65fbac 100644 --- a/core/write.go +++ b/core/write.go @@ -7,6 +7,9 @@ import ( "strings" ) +// the tag used to denote the name of the question +const tagName = "survey" + func WriteAnswer(t interface{}, name string, v interface{}) (err error) { // the target to write to target := reflect.ValueOf(t) @@ -52,6 +55,14 @@ func findFieldIndex(s reflect.Value, name string) (int, error) { if strings.ToLower(field.Name) == name { return i, nil } + + // the value of the survey tag + tag := field.Tag.Get(tagName) + // if the tag matches the name we are looking for + if tag != "" && tag == name { + // then we found our index + return i, nil + } } // we didn't find the field return -1, fmt.Errorf("could not find field matching %v", name) diff --git a/core/write_test.go b/core/write_test.go index 288f9d1d..ff4787ff 100644 --- a/core/write_test.go +++ b/core/write_test.go @@ -137,11 +137,8 @@ func TestWriteAnswer_returnsErrWhenFieldNotFound(t *testing.T) { } func TestFindFieldIndex_canFindExportedField(t *testing.T) { - // the struct to look through - ptr := struct{ Name string }{} - - // create a reflective wrapper over the value - val := reflect.ValueOf(ptr) + // create a reflective wrapper over the struct to look through + val := reflect.ValueOf(struct{ Name string }{}) // find the field matching "name" fieldIndex, err := findFieldIndex(val, "name") @@ -158,3 +155,25 @@ func TestFindFieldIndex_canFindExportedField(t *testing.T) { t.Errorf("Did not find the correct field name. Expected 'Name' found %v.", val.Type().Field(fieldIndex).Name) } } + +func TestFindFieldIndex_canFindTaggedField(t *testing.T) { + // the struct to look through + val := reflect.ValueOf(struct { + Username string `survey:"name"` + }{}) + + // find the field matching "name" + fieldIndex, err := findFieldIndex(val, "name") + // if something went wrong + if err != nil { + // the test failed + t.Error(err.Error()) + return + } + + // make sure we got the right value + if val.Type().Field(fieldIndex).Name != "Username" { + // the test failed + t.Errorf("Did not find the correct field name. Expected 'Username' found %v.", val.Type().Field(fieldIndex).Name) + } +} From e8ffedd574a21787943a49cef869512451d001c1 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 12:07:20 -0700 Subject: [PATCH 10/42] tag/name conflicts now go to the tag --- core/write.go | 20 ++++++++++++++------ core/write_test.go | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/core/write.go b/core/write.go index dd65fbac..a7148468 100644 --- a/core/write.go +++ b/core/write.go @@ -46,16 +46,12 @@ func WriteAnswer(t interface{}, name string, v interface{}) (err error) { func findFieldIndex(s reflect.Value, name string) (int, error) { // the type of the value sType := s.Type() - // scan the fields of the struct + + // first look for matching tags for i := 0; i < sType.NumField(); i++ { // the field we are current scanning field := sType.Field(i) - // if the name of the field matches what we're looking for - if strings.ToLower(field.Name) == name { - return i, nil - } - // the value of the survey tag tag := field.Tag.Get(tagName) // if the tag matches the name we are looking for @@ -64,6 +60,18 @@ func findFieldIndex(s reflect.Value, name string) (int, error) { return i, nil } } + + // then look for matching names + for i := 0; i < sType.NumField(); i++ { + // the field we are current scanning + field := sType.Field(i) + + // if the name of the field matches what we're looking for + if strings.ToLower(field.Name) == name { + return i, nil + } + } + // we didn't find the field return -1, fmt.Errorf("could not find field matching %v", name) } diff --git a/core/write_test.go b/core/write_test.go index ff4787ff..7c327505 100644 --- a/core/write_test.go +++ b/core/write_test.go @@ -177,3 +177,26 @@ func TestFindFieldIndex_canFindTaggedField(t *testing.T) { t.Errorf("Did not find the correct field name. Expected 'Username' found %v.", val.Type().Field(fieldIndex).Name) } } + +func TestFindFieldIndex_tagOverwriteFieldName(t *testing.T) { + // the struct to look through + val := reflect.ValueOf(struct { + Name string + Username string `survey:"name"` + }{}) + + // find the field matching "name" + fieldIndex, err := findFieldIndex(val, "name") + // if something went wrong + if err != nil { + // the test failed + t.Error(err.Error()) + return + } + + // make sure we got the right value + if val.Type().Field(fieldIndex).Name != "Username" { + // the test failed + t.Errorf("Did not find the correct field name. Expected 'Username' found %v.", val.Type().Field(fieldIndex).Name) + } +} From 57d2519bc9a1a2fdbc057ac8533512f671a11bd8 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 12:11:46 -0700 Subject: [PATCH 11/42] capital answer names are now matched to fields --- core/write.go | 4 ++-- core/write_test.go | 23 ++++++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/core/write.go b/core/write.go index a7148468..46c3c852 100644 --- a/core/write.go +++ b/core/write.go @@ -47,7 +47,7 @@ func findFieldIndex(s reflect.Value, name string) (int, error) { // the type of the value sType := s.Type() - // first look for matching tags + // first look for matching tags so we can overwrite matching field names for i := 0; i < sType.NumField(); i++ { // the field we are current scanning field := sType.Field(i) @@ -67,7 +67,7 @@ func findFieldIndex(s reflect.Value, name string) (int, error) { field := sType.Field(i) // if the name of the field matches what we're looking for - if strings.ToLower(field.Name) == name { + if strings.ToLower(field.Name) == strings.ToLower(name) { return i, nil } } diff --git a/core/write_test.go b/core/write_test.go index 7c327505..a410df3a 100644 --- a/core/write_test.go +++ b/core/write_test.go @@ -1,7 +1,6 @@ package core import ( - "fmt" "reflect" "testing" ) @@ -114,8 +113,6 @@ func TestWriteAnswer_canMutateStruct(t *testing.T) { return } - fmt.Println(ptr.Name) - // make sure we changed the field if ptr.Name != "world" { // the test failed @@ -178,6 +175,26 @@ func TestFindFieldIndex_canFindTaggedField(t *testing.T) { } } +func TestFindFieldIndex_canHandleCapitalAnswerNames(t *testing.T) { + // create a reflective wrapper over the struct to look through + val := reflect.ValueOf(struct{ Name string }{}) + + // find the field matching "name" + fieldIndex, err := findFieldIndex(val, "Name") + // if something went wrong + if err != nil { + // the test failed + t.Error(err.Error()) + return + } + + // make sure we got the right value + if val.Type().Field(fieldIndex).Name != "Name" { + // the test failed + t.Errorf("Did not find the correct field name. Expected 'Name' found %v.", val.Type().Field(fieldIndex).Name) + } +} + func TestFindFieldIndex_tagOverwriteFieldName(t *testing.T) { // the struct to look through val := reflect.ValueOf(struct { From 3bb4949a782d4312aaee9abfa3ba5d79e8e108db Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 12:14:38 -0700 Subject: [PATCH 12/42] added bug notice --- core/write.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/write.go b/core/write.go index 46c3c852..a44722e8 100644 --- a/core/write.go +++ b/core/write.go @@ -43,6 +43,8 @@ func WriteAnswer(t interface{}, name string, v interface{}) (err error) { return copy(elem, value) } +// BUG(alecaivazis): the current implementation might cause weird conflicts if there are +// two fields with same name that only differ by casing. func findFieldIndex(s reflect.Value, name string) (int, error) { // the type of the value sType := s.Type() From 66dcda74bc6827cd64e94f0302839af2c12ab6d5 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 17:16:39 -0700 Subject: [PATCH 13/42] ask functions now take pointers to obj to write answers --- input.go | 2 +- survey.go | 44 +++++++++++++++++--------------------------- tests/ask.go | 17 +++++++++++------ 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/input.go b/input.go index 872fc90f..8137ac97 100644 --- a/input.go +++ b/input.go @@ -52,7 +52,7 @@ func (i *Input) Cleanup(rl *readline.Instance, val string) error { // go up one line terminal.CursorPreviousLine(1) // clear the line - terminal.EraseInLine(1) + terminal.EraseInLine(0) // render the template out, err := core.RunTemplate( diff --git a/survey.go b/survey.go index f2c0c03e..feff0244 100644 --- a/survey.go +++ b/survey.go @@ -2,7 +2,6 @@ package survey import ( "fmt" - "os" "github.com/alecaivazis/survey/core" "github.com/alecaivazis/survey/terminal" @@ -30,44 +29,30 @@ var ErrorTemplate = `{{color "red"}}✘ Sorry, your reply was invalid: {{.Error} ` // AskOne asks a single question without performing validation on the answer. -func AskOne(p Prompt) (string, error) { - answers, err := Ask([]*Question{{Name: "q1", Prompt: p}}) +func AskOne(p Prompt, t interface{}, v Validator) error { + err := Ask([]*Question{{Prompt: p, Validate: v}}, t) if err != nil { - return "", err + return err } - return answers["q1"], nil -} - -// AskOneValidate asks a single question and validates the answer with v. -func AskOneValidate(p Prompt, v Validator) (string, error) { - answers, err := Ask([]*Question{{Name: "q1", Prompt: p, Validate: v}}) - return answers["q1"], err -} -func handleError(err error) { - // tell the user what happened - fmt.Println(err.Error()) - // quit the survey - os.Exit(1) + return nil } // Ask performs the prompt loop -func Ask(qs []*Question) (map[string]string, error) { +func Ask(qs []*Question, t interface{}) error { // grab the readline instance rl, err := terminal.GetReadline() if err != nil { - handleError(err) + return err } - // the response map - res := make(map[string]string) // go over every question for _, q := range qs { // grab the user input and save it ans, err := q.Prompt.Prompt(rl) // if there was a problem if err != nil { - handleError(err) + return err } // if there is a validate handler for this question @@ -76,7 +61,7 @@ func Ask(qs []*Question) (map[string]string, error) { for invalid := q.Validate(ans); invalid != nil; invalid = q.Validate(ans) { out, err := core.RunTemplate(ErrorTemplate, invalid) if err != nil { - return nil, err + return err } // send the message to the user fmt.Print(out) @@ -84,7 +69,7 @@ func Ask(qs []*Question) (map[string]string, error) { ans, err = q.Prompt.Prompt(rl) // if there was a problem if err != nil { - handleError(err) + return err } } } @@ -95,11 +80,16 @@ func Ask(qs []*Question) (map[string]string, error) { // if something went wrong if err != nil { // stop listening - return nil, err + return err } + // add it to the map - res[q.Name] = ans + err = core.WriteAnswer(t, q.Name, ans) + // if something went wrong + if err != nil { + return err + } } // return the response - return res, nil + return nil } diff --git a/tests/ask.go b/tests/ask.go index bbf71731..ea6dcf41 100644 --- a/tests/ask.go +++ b/tests/ask.go @@ -29,16 +29,20 @@ var simpleQs = []*survey.Question{ func main() { fmt.Println("Asking many.") - - answers, err := survey.Ask(simpleQs) + // a place to store the answers + ans := struct { + Name string + Color string + }{} + err := survey.Ask(simpleQs, &ans) if err != nil { fmt.Println(err.Error()) return } - fmt.Printf("%s chose %s.\n", answers["name"], answers["color"]) fmt.Println("Asking one.") - answer, err := survey.AskOne(simpleQs[0].Prompt) + answer := "" + err = survey.AskOne(simpleQs[0].Prompt, &answer, nil) if err != nil { fmt.Println(err.Error()) return @@ -46,10 +50,11 @@ func main() { fmt.Printf("Answered with %v.\n", answer) fmt.Println("Asking one with validation.") - answer, err = survey.AskOneValidate(&survey.Input{"What is your name?", ""}, survey.Required) + vAns := "" + err = survey.AskOne(&survey.Input{"What is your name?", ""}, &vAns, survey.Required) if err != nil { fmt.Println(err.Error()) return } - fmt.Printf("Answered with %v.\n", answer) + fmt.Printf("Answered with %v.\n", vAns) } From 17cd76b3e0d4d825ba454156d22fd29d47ae46de Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 17:21:35 -0700 Subject: [PATCH 14/42] cannot run Ask with a nil receiver --- survey.go | 7 +++++++ survey_test.go | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/survey.go b/survey.go index feff0244..a6efc12b 100644 --- a/survey.go +++ b/survey.go @@ -1,6 +1,7 @@ package survey import ( + "errors" "fmt" "github.com/alecaivazis/survey/core" @@ -46,6 +47,12 @@ func Ask(qs []*Question, t interface{}) error { return err } + // if we weren't passed a place to record the answers + if t == nil { + // we can't go any further + return errors.New("cannot call Ask() with a nil reference to record the answers") + } + // go over every question for _, q := range qs { // grab the user input and save it diff --git a/survey_test.go b/survey_test.go index e73765aa..e457fa22 100644 --- a/survey_test.go +++ b/survey_test.go @@ -31,3 +31,14 @@ func TestValidationError(t *testing.T) { t.Errorf("Formatted error was not formatted correctly. Found:\n%s\nExpected:\n%s", actual, expected) } } + +func TestAsk_returnsErrorIfTargetIsNil(t *testing.T) { + // pass an empty place to leave the answers + err := Ask([]*Question{}, nil) + + // if we didn't get an error + if err == nil { + // the test failed + t.Error("Did not encounter error when asking with no where to record.") + } +} From 9c6b3071fcf59f406c287433875951046b005b24 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 17:40:41 -0700 Subject: [PATCH 15/42] migrated confirm to new api --- confirm.go | 22 ++++++++++++++-------- examples/confirm.go | 6 +++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/confirm.go b/confirm.go index 1a5c6de6..8bdf50a0 100644 --- a/confirm.go +++ b/confirm.go @@ -89,7 +89,7 @@ func (c *Confirm) getBool(rl *readline.Instance) (bool, error) { // Prompt prompts the user with a simple text field and expects a reply followed // by a carriage return. -func (c *Confirm) Prompt(rl *readline.Instance) (string, error) { +func (c *Confirm) Prompt(rl *readline.Instance) (interface{}, error) { // if we weren't passed an answer if c.Answer == nil { // build one @@ -117,24 +117,30 @@ func (c *Confirm) Prompt(rl *readline.Instance) (string, error) { return "", err } - // return the value - *c.Answer = answer - // convert the boolean into the appropriate string - return yesNo(answer), nil + return answer, nil } // Cleanup overwrite the line with the finalized formatted version -func (c *Confirm) Cleanup(rl *readline.Instance, val string) error { +func (c *Confirm) Cleanup(rl *readline.Instance, val interface{}) error { // go up one line terminal.CursorPreviousLine(1) // clear the line - terminal.EraseInLine(1) + terminal.EraseInLine(0) + + // the string version of the answer + ans := "" + // if the value was previously true + if val.(bool) { + ans = "true" + } else { + ans = "false" + } // render the template out, err := core.RunTemplate( ConfirmQuestionTemplate, - ConfirmTemplateData{Confirm: *c, Answer: val}, + ConfirmTemplateData{Confirm: *c, Answer: ans}, ) if err != nil { return err diff --git a/examples/confirm.go b/examples/confirm.go index d90ca8d8..c38e46c7 100644 --- a/examples/confirm.go +++ b/examples/confirm.go @@ -10,13 +10,13 @@ func main() { prompt := &survey.Confirm{ Message: "Are you happy?", } - - answer, err := survey.AskOne(prompt) + ans := false + err := survey.AskOne(prompt, &ans, nil) if err != nil { fmt.Println(err.Error()) return } - fmt.Printf("response string: %s\n", answer) + fmt.Printf("response: %v\n", ans) } From 102baaf03318551c03e7901e285fafa7fefb37c3 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 17:40:58 -0700 Subject: [PATCH 16/42] migrate input to new api --- input.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/input.go b/input.go index 8137ac97..bd85de79 100644 --- a/input.go +++ b/input.go @@ -31,7 +31,7 @@ var InputQuestionTemplate = ` {{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}} {{- end}}` -func (i *Input) Prompt(rl *readline.Instance) (line string, err error) { +func (i *Input) Prompt(rl *readline.Instance) (line interface{}, err error) { // render the template out, err := core.RunTemplate( InputQuestionTemplate, @@ -48,7 +48,7 @@ func (i *Input) Prompt(rl *readline.Instance) (line string, err error) { return line, err } -func (i *Input) Cleanup(rl *readline.Instance, val string) error { +func (i *Input) Cleanup(rl *readline.Instance, val interface{}) error { // go up one line terminal.CursorPreviousLine(1) // clear the line @@ -57,7 +57,7 @@ func (i *Input) Cleanup(rl *readline.Instance, val string) error { // render the template out, err := core.RunTemplate( InputQuestionTemplate, - InputTemplateData{Input: *i, Answer: val}, + InputTemplateData{Input: *i, Answer: val.(string)}, ) if err != nil { return err From dcb991903c2f882c5b594bc78e5313e692bdd3de Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 17:45:40 -0700 Subject: [PATCH 17/42] migrated multichoice to reflection based api --- examples/multichoice.go | 6 ++---- multichoice.go | 47 ++++++++--------------------------------- 2 files changed, 11 insertions(+), 42 deletions(-) diff --git a/examples/multichoice.go b/examples/multichoice.go index 17ba0f01..f6366ee0 100644 --- a/examples/multichoice.go +++ b/examples/multichoice.go @@ -12,16 +12,14 @@ func main() { Message: "What days do you prefer:", Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, Defaults: []string{"Saturday", "Sunday"}, - Answer: &days, } - answer, err := survey.AskOne(prompt) + err := survey.AskOne(prompt, &days, nil) if err != nil { fmt.Println(err.Error()) return } - fmt.Printf("response string (json for MultiChoice): %s\n", answer) - fmt.Printf("response days: %#v\n", days) + fmt.Printf("response: %s\n", days) } diff --git a/multichoice.go b/multichoice.go index 19ba8234..8f91e70d 100644 --- a/multichoice.go +++ b/multichoice.go @@ -1,7 +1,6 @@ package survey import ( - "encoding/json" "errors" "io/ioutil" "strings" @@ -25,7 +24,7 @@ type MultiChoice struct { // data available to the templates when processing type MultiChoiceTemplateData struct { MultiChoice - Answer []string + Answer string Checked map[int]bool SelectedIndex int } @@ -33,7 +32,7 @@ type MultiChoiceTemplateData struct { var MultiChoiceQuestionTemplate = ` {{- color "green+hb"}}? {{color "reset"}} {{- color "default+hb"}}{{ .Message }} {{color "reset"}} -{{- if .Answer}}{{color "cyan"}}{{.Answer | printf "%q"}}{{color "reset"}}{{end}}` +{{- if .Answer}}{{color "cyan"}}{{.Answer}}{{color "reset"}}{{end}}` var MultiChoiceOptionsTemplate = ` {{- range $ix, $option := .Options}} @@ -77,7 +76,7 @@ func (m *MultiChoice) render() error { // clean up what we left behind last time for range m.Options { terminal.CursorPreviousLine(1) - terminal.EraseInLine(1) + terminal.EraseInLine(0) } // render the template summarizing the current state @@ -100,14 +99,7 @@ func (m *MultiChoice) render() error { return nil } -func (m *MultiChoice) Prompt(rl *readline.Instance) (string, error) { - // if the user didn't pass an answer reference - if m.Answer == nil { - // build one - answer := []string{} - m.Answer = &answer - } - +func (m *MultiChoice) Prompt(rl *readline.Instance) (interface{}, error) { // the readline config config := &readline.Config{ Listener: m, @@ -172,40 +164,19 @@ func (m *MultiChoice) Prompt(rl *readline.Instance) (string, error) { answers = append(answers, option) } } - *m.Answer = answers - // nothing went wrong - return m.value() -} - -func (m *MultiChoice) value() (string, error) { - answers := []string{} - for ix, option := range m.Options { - if val, ok := m.checked[ix]; ok && val { - answers = append(answers, option) - } - } - // return the selected option - js, err := json.Marshal(answers) - if err != nil { - return "", err - } - return string(js), nil + return answers, nil } // Cleanup removes the options section, and renders the ask like a normal question. -func (m *MultiChoice) Cleanup(rl *readline.Instance, val string) error { +func (m *MultiChoice) Cleanup(rl *readline.Instance, val interface{}) error { terminal.CursorPreviousLine(1) - terminal.EraseInLine(1) + terminal.EraseInLine(0) for range m.Options { terminal.CursorPreviousLine(1) - terminal.EraseInLine(1) + terminal.EraseInLine(0) } - // parse the value into a list of strings - var value []string - json.Unmarshal([]byte(val), &value) - // execute the output summary template with the answer output, err := core.RunTemplate( MultiChoiceQuestionTemplate, @@ -213,7 +184,7 @@ func (m *MultiChoice) Cleanup(rl *readline.Instance, val string) error { MultiChoice: *m, SelectedIndex: m.selectedIndex, Checked: m.checked, - Answer: value, + Answer: strings.Join(val.([]string), ","), }, ) if err != nil { From 06a110be498dbb443583dec0ee3f0312434e41c0 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 17:46:59 -0700 Subject: [PATCH 18/42] converted password to reflection api --- password.go | 4 ++-- tests/password.go | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/password.go b/password.go index b4d95a65..c4619b22 100644 --- a/password.go +++ b/password.go @@ -17,7 +17,7 @@ var PasswordQuestionTemplate = ` {{- color "green+hb"}}? {{color "reset"}} {{- color "default+hb"}}{{ .Message }} {{color "reset"}}` -func (p *Password) Prompt(rl *readline.Instance) (line string, err error) { +func (p *Password) Prompt(rl *readline.Instance) (line interface{}, err error) { // render the question template out, err := core.RunTemplate( PasswordQuestionTemplate, @@ -41,6 +41,6 @@ func (p *Password) Prompt(rl *readline.Instance) (line string, err error) { } // Cleanup hides the string with a fixed number of characters. -func (prompt *Password) Cleanup(rl *readline.Instance, val string) error { +func (prompt *Password) Cleanup(rl *readline.Instance, val interface{}) error { return nil } diff --git a/tests/password.go b/tests/password.go index 92fba2b7..dc2c7b27 100644 --- a/tests/password.go +++ b/tests/password.go @@ -5,12 +5,14 @@ import ( "github.com/alecaivazis/survey/tests/util" ) +var value = "" + var table = []TestUtil.TestTableEntry{ { - "standard", &survey.Password{"Please type your password:"}, + "standard", &survey.Password{"Please type your password:"}, &value, }, { - "please make sure paste works", &survey.Password{"Please paste your password:"}, + "please make sure paste works", &survey.Password{"Please paste your password:"}, &value, }, } From d96bc3cf6c974d608b51a7bedadecca5f1d7a72d Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 17:47:51 -0700 Subject: [PATCH 19/42] upgraded select to new api --- select.go | 12 ++++++------ tests/select.go | 12 +++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/select.go b/select.go index 2268d774..fc66db33 100644 --- a/select.go +++ b/select.go @@ -64,7 +64,7 @@ func (s *Select) OnChange(line []rune, pos int, key rune) (newLine []rune, newPo func (s *Select) render() error { for range s.Options { terminal.CursorPreviousLine(1) - terminal.EraseInLine(1) + terminal.EraseInLine(0) } // the formatted response @@ -82,7 +82,7 @@ func (s *Select) render() error { return nil } -func (s *Select) Prompt(rl *readline.Instance) (string, error) { +func (s *Select) Prompt(rl *readline.Instance) (interface{}, error) { config := &readline.Config{ Listener: s, Stdout: ioutil.Discard, @@ -150,18 +150,18 @@ func (s *Select) Prompt(rl *readline.Instance) (string, error) { return val, err } -func (s *Select) Cleanup(rl *readline.Instance, val string) error { +func (s *Select) Cleanup(rl *readline.Instance, val interface{}) error { terminal.CursorPreviousLine(1) - terminal.EraseInLine(1) + terminal.EraseInLine(0) for range s.Options { terminal.CursorPreviousLine(1) - terminal.EraseInLine(1) + terminal.EraseInLine(0) } // execute the output summary template with the answer output, err := core.RunTemplate( SelectQuestionTemplate, - SelectTemplateData{Select: *s, Answer: val}, + SelectTemplateData{Select: *s, Answer: val.(string)}, ) if err != nil { return err diff --git a/tests/select.go b/tests/select.go index ba6e7c34..e9bea9b6 100644 --- a/tests/select.go +++ b/tests/select.go @@ -5,31 +5,33 @@ import ( "github.com/alecaivazis/survey/tests/util" ) +var answer = "" + var goodTable = []TestUtil.TestTableEntry{ { "standard", &survey.Select{ Message: "Choose a color:", Options: []string{"red", "blue", "green"}, - }, + }, &answer, }, { "short", &survey.Select{ Message: "Choose a color:", Options: []string{"red", "blue"}, - }, + }, &answer, }, { "default", &survey.Select{ Message: "Choose a color (should default blue):", Options: []string{"red", "blue", "green"}, Default: "blue", - }, + }, &answer, }, { "one", &survey.Select{ Message: "Choose one:", Options: []string{"hello"}, - }, + }, &answer, }, } @@ -37,7 +39,7 @@ var badTable = []TestUtil.TestTableEntry{ { "no options", &survey.Select{ Message: "Choose one:", - }, + }, &answer, }, } From 4d242cfc78aaea08f936f681272cb17cf3b57432 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 17:48:16 -0700 Subject: [PATCH 20/42] updated interfaces for generic return types --- survey.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/survey.go b/survey.go index a6efc12b..62a53057 100644 --- a/survey.go +++ b/survey.go @@ -10,7 +10,7 @@ import ( ) // Validator is a function passed to a Question in order to redefine -type Validator func(string) error +type Validator func(interface{}) error // Question is the core data structure for a survey questionnaire. type Question struct { @@ -22,8 +22,8 @@ type Question struct { // Prompt is the primary interface for the objects that can take user input // and return a string value. type Prompt interface { - Prompt(*readline.Instance) (string, error) - Cleanup(*readline.Instance, string) error + Prompt(*readline.Instance) (interface{}, error) + Cleanup(*readline.Instance, interface{}) error } var ErrorTemplate = `{{color "red"}}✘ Sorry, your reply was invalid: {{.Error}}{{color "reset"}} From 9aa2ad3ba0161fce5dc52f7940e4986d6865d1b6 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 17:48:52 -0700 Subject: [PATCH 21/42] updated test table runner to match new prompt interface --- tests/util/test.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/util/test.go b/tests/util/test.go index f70282a1..15708dd6 100644 --- a/tests/util/test.go +++ b/tests/util/test.go @@ -2,6 +2,7 @@ package TestUtil import ( "fmt" + "reflect" "github.com/alecaivazis/survey" ) @@ -9,11 +10,12 @@ import ( type TestTableEntry struct { Name string Prompt survey.Prompt + Value interface{} } -func formatAnswer(ans string) { +func formatAnswer(ans interface{}) { // show the answer to the user - fmt.Printf("Answered %v.\n", ans) + fmt.Printf("Answered %v.\n", reflect.ValueOf(ans).Elem()) fmt.Println("---------------------") } @@ -23,13 +25,13 @@ func RunTable(table []TestTableEntry) { // tell the user what we are going to ask them fmt.Println(entry.Name) // perform the ask - answer, err := survey.AskOne(entry.Prompt) + err := survey.AskOne(entry.Prompt, entry.Value, nil) if err != nil { fmt.Printf("AskOne on %v's prompt failed: %v.", entry.Name, err.Error()) break } // show the answer to the user - formatAnswer(answer) + formatAnswer(entry.Value) } } @@ -39,7 +41,7 @@ func RunErrorTable(table []TestTableEntry) { // tell the user what we are going to ask them fmt.Println(entry.Name) // perform the ask - _, err := survey.AskOne(entry.Prompt) + err := survey.AskOne(entry.Prompt, entry.Value, nil) if err == nil { fmt.Printf("AskOne on %v's prompt didn't fail.", entry.Name) break From 70b9a51f032a3d858d700d600b0084535d3235dc Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 17:49:04 -0700 Subject: [PATCH 22/42] fixed bug in input tests --- tests/input.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/input.go b/tests/input.go index 9332ccc0..ebbe6ec1 100644 --- a/tests/input.go +++ b/tests/input.go @@ -5,12 +5,14 @@ import ( "github.com/alecaivazis/survey/tests/util" ) +var val = "" + var table = []TestUtil.TestTableEntry{ { - "no default", &survey.Input{"Hello world", ""}, + "no default", &survey.Input{"Hello world", ""}, &val, }, { - "default", &survey.Input{"Hello world", "default"}, + "default", &survey.Input{"Hello world", "default"}, &val, }, } From 0e6f81e13463740ee98f13f7aaa96c2b07849f33 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 18:06:15 -0700 Subject: [PATCH 23/42] validators now accomodate generic inputs --- examples/validation.go | 19 +++++++++++------- multichoice_test.go | 5 +++-- validate.go | 45 +++++++++++++++++++++++++++--------------- validate_test.go | 14 +++++++++++++ 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/examples/validation.go b/examples/validation.go index bc0b758c..49a90700 100644 --- a/examples/validation.go +++ b/examples/validation.go @@ -3,6 +3,8 @@ package main import ( "errors" "fmt" + "reflect" + "github.com/alecaivazis/survey" ) @@ -16,20 +18,23 @@ var validationQs = []*survey.Question{ { Name: "valid", Prompt: &survey.Input{"Enter 'foo':", "not foo"}, - Validate: func(str string) error { - // if the input matches the expectation - if str != "foo" { - return errors.New(fmt.Sprintf("You entered %s, not 'foo'.", str)) + Validate: func(val interface{}) error { + // if the value passed in is the zero value of the appropriate type + if val == reflect.Zero(reflect.TypeOf(val)).Interface() { + return errors.New("Value is required") } - // nothing was wrong return nil }, }, } func main() { - - _, err := survey.Ask(validationQs) + // the place to hold the answers + answers := struct { + Name string + Valid string + }{} + err := survey.Ask(validationQs, &answers) if err != nil { fmt.Println("\n", err.Error()) diff --git a/multichoice_test.go b/multichoice_test.go index 79486752..56e363a8 100644 --- a/multichoice_test.go +++ b/multichoice_test.go @@ -1,6 +1,7 @@ package survey import ( + "strings" "testing" "github.com/alecaivazis/survey/core" @@ -75,13 +76,13 @@ func TestMultiChoiceFormatAnswer(t *testing.T) { actual, err := core.RunTemplate( MultiChoiceQuestionTemplate, - MultiChoiceTemplateData{MultiChoice: *prompt, Answer: []string{"foo", "buz"}}, + MultiChoiceTemplateData{MultiChoice: *prompt, Answer: strings.Join([]string{"foo", "buz"}, ", ")}, ) if err != nil { t.Errorf("Failed to run template to format checkbox answer: %s", err) } - expected := `? Pick your words: ["foo" "buz"]` + expected := `? Pick your words: foo, buz` if actual != expected { t.Errorf("Formatted checkbox answer was not formatted correctly. Found:\n%s\nExpected:\n%s", actual, expected) diff --git a/validate.go b/validate.go index d4693409..6e275beb 100644 --- a/validate.go +++ b/validate.go @@ -3,27 +3,33 @@ package survey import ( "errors" "fmt" + "reflect" ) // Required does not allow an empty value -func Required(str string) error { - // if the string is empty - if str == "" { - // return the error - return errors.New("Value is required.") +func Required(val interface{}) error { + // if the value passed in is the zero value of the appropriate type + if val == reflect.Zero(reflect.TypeOf(val)).Interface() { + return errors.New("Value is required") } - // nothing was wrong return nil } // MaxLength requires that the string is no longer than the specified value func MaxLength(length int) Validator { // return a validator that checks the length of the string - return func(str string) error { - // if the string is longer than the given value - if len(str) > length { - return fmt.Errorf("Value is too long. Max length is %v.", length) + return func(val interface{}) error { + if str, ok := val.(string); ok { + // if the string is longer than the given value + if len(str) > length { + // yell loudly + return fmt.Errorf("value is too long. Max length is %v", length) + } + } else { + // otherwise we cannot convert the value into a string and cannot enforce length + return fmt.Errorf("cannot enforce length on response of type %v", reflect.TypeOf(val).Name()) } + // the input is fine return nil } @@ -32,11 +38,18 @@ func MaxLength(length int) Validator { // MinLength requires that the string is longer or equal in length to the specified value func MinLength(length int) Validator { // return a validator that checks the length of the string - return func(str string) error { - // if the string is longer than the given value - if len(str) < length { - return fmt.Errorf("Value is too short. Min length is %v.", length) + return func(val interface{}) error { + if str, ok := val.(string); ok { + // if the string is shorter than the given value + if len(str) < length { + // yell loudly + return fmt.Errorf("value is too short. Min length is %v", length) + } + } else { + // otherwise we cannot convert the value into a string and cannot enforce length + return fmt.Errorf("cannot enforce length on response of type %v", reflect.TypeOf(val).Name()) } + // the input is fine return nil } @@ -45,11 +58,11 @@ func MinLength(length int) Validator { // ComposeValidators is a variadic function used to create one validator from many. func ComposeValidators(validators ...Validator) Validator { // return a validator that calls each one sequentially - return func(str string) error { + return func(val interface{}) error { // execute each validator for _, validator := range validators { // if the string is not valid - if err := validator(str); err != nil { + if err := validator(val); err != nil { // return the error return err } diff --git a/validate_test.go b/validate_test.go index ed5f0369..9b19500d 100644 --- a/validate_test.go +++ b/validate_test.go @@ -52,6 +52,20 @@ func TestMinLength(t *testing.T) { } } +func TestMinLengthOnInt(t *testing.T) { + // validate the string + if err := MinLength(12)(1); err == nil { + t.Error("No error returned when enforcing length on int.") + } +} + +func TestMaxLengthOnInt(t *testing.T) { + // validate the string + if err := MaxLength(12)(1); err == nil { + t.Error("No error returned when enforcing length on int.") + } +} + func TestComposeValidatorsPasses(t *testing.T) { // create a validator that requires a string of no more than 10 characters valid := ComposeValidators( From e0b680a1d7ed80ca7b6324555d6d92ac407ae91c Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 18:08:27 -0700 Subject: [PATCH 24/42] cleaned up validation exaples --- examples/validation.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/validation.go b/examples/validation.go index 49a90700..998cd9c6 100644 --- a/examples/validation.go +++ b/examples/validation.go @@ -1,9 +1,7 @@ package main import ( - "errors" "fmt" - "reflect" "github.com/alecaivazis/survey" ) @@ -19,10 +17,11 @@ var validationQs = []*survey.Question{ Name: "valid", Prompt: &survey.Input{"Enter 'foo':", "not foo"}, Validate: func(val interface{}) error { - // if the value passed in is the zero value of the appropriate type - if val == reflect.Zero(reflect.TypeOf(val)).Interface() { - return errors.New("Value is required") + // if the input matches the expectation + if str := val.(string); str != "foo" { + return fmt.Errorf("You entered %s, not 'foo'.", str) } + // nothing was wrong return nil }, }, From 618b75e7a9705bbda699f008dc2186bf280b1037 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 20:07:47 -0700 Subject: [PATCH 25/42] renamed multichoice to multiselect --- examples/{multichoice.go => multiselect.go} | 2 +- multichoice.go => multiselect.go | 38 ++++++++++----------- multichoice_test.go => multiselect_test.go | 26 +++++++------- 3 files changed, 33 insertions(+), 33 deletions(-) rename examples/{multichoice.go => multiselect.go} (93%) rename multichoice.go => multiselect.go (85%) rename multichoice_test.go => multiselect_test.go (76%) diff --git a/examples/multichoice.go b/examples/multiselect.go similarity index 93% rename from examples/multichoice.go rename to examples/multiselect.go index f6366ee0..8bba406a 100644 --- a/examples/multichoice.go +++ b/examples/multiselect.go @@ -8,7 +8,7 @@ import ( func main() { days := []string{} - prompt := &survey.MultiChoice{ + prompt := &survey.MultiSelect{ Message: "What days do you prefer:", Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, Defaults: []string{"Saturday", "Sunday"}, diff --git a/multichoice.go b/multiselect.go similarity index 85% rename from multichoice.go rename to multiselect.go index 8f91e70d..ebdf9bf2 100644 --- a/multichoice.go +++ b/multiselect.go @@ -10,9 +10,9 @@ import ( "github.com/chzyer/readline" ) -// MultiChoice is a prompt that presents a list of various options to the user +// MultiSelect is a prompt that presents a list of various options to the user // for them to select using the arrow keys and enter. -type MultiChoice struct { +type MultiSelect struct { Message string Options []string Defaults []string @@ -22,19 +22,19 @@ type MultiChoice struct { } // data available to the templates when processing -type MultiChoiceTemplateData struct { - MultiChoice +type MultiSelectTemplateData struct { + MultiSelect Answer string Checked map[int]bool SelectedIndex int } -var MultiChoiceQuestionTemplate = ` +var MultiSelectQuestionTemplate = ` {{- color "green+hb"}}? {{color "reset"}} {{- color "default+hb"}}{{ .Message }} {{color "reset"}} {{- if .Answer}}{{color "cyan"}}{{.Answer}}{{color "reset"}}{{end}}` -var MultiChoiceOptionsTemplate = ` +var MultiSelectOptionsTemplate = ` {{- range $ix, $option := .Options}} {{- if eq $ix $.SelectedIndex}}{{color "cyan"}}❯{{color "reset"}}{{else}} {{end}} {{- if index $.Checked $ix}}{{color "green"}} ◉ {{else}}{{color "default+hb"}} ◯ {{end}} @@ -43,7 +43,7 @@ var MultiChoiceOptionsTemplate = ` {{end}}` // OnChange is called on every keypress. -func (m *MultiChoice) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) { +func (m *MultiSelect) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) { if key == terminal.KeyEnter { // just pass on the current value return line, 0, true @@ -72,7 +72,7 @@ func (m *MultiChoice) OnChange(line []rune, pos int, key rune) (newLine []rune, return line, 0, true } -func (m *MultiChoice) render() error { +func (m *MultiSelect) render() error { // clean up what we left behind last time for range m.Options { terminal.CursorPreviousLine(1) @@ -81,9 +81,9 @@ func (m *MultiChoice) render() error { // render the template summarizing the current state out, err := core.RunTemplate( - MultiChoiceOptionsTemplate, - MultiChoiceTemplateData{ - MultiChoice: *m, + MultiSelectOptionsTemplate, + MultiSelectTemplateData{ + MultiSelect: *m, SelectedIndex: m.selectedIndex, Checked: m.checked, }, @@ -99,7 +99,7 @@ func (m *MultiChoice) render() error { return nil } -func (m *MultiChoice) Prompt(rl *readline.Instance) (interface{}, error) { +func (m *MultiSelect) Prompt(rl *readline.Instance) (interface{}, error) { // the readline config config := &readline.Config{ Listener: m, @@ -131,9 +131,9 @@ func (m *MultiChoice) Prompt(rl *readline.Instance) (interface{}, error) { } // generate the template for the current state of the prompt out, err := core.RunTemplate( - MultiChoiceQuestionTemplate, - MultiChoiceTemplateData{ - MultiChoice: *m, + MultiSelectQuestionTemplate, + MultiSelectTemplateData{ + MultiSelect: *m, SelectedIndex: m.selectedIndex, Checked: m.checked, }, @@ -169,7 +169,7 @@ func (m *MultiChoice) Prompt(rl *readline.Instance) (interface{}, error) { } // Cleanup removes the options section, and renders the ask like a normal question. -func (m *MultiChoice) Cleanup(rl *readline.Instance, val interface{}) error { +func (m *MultiSelect) Cleanup(rl *readline.Instance, val interface{}) error { terminal.CursorPreviousLine(1) terminal.EraseInLine(0) for range m.Options { @@ -179,9 +179,9 @@ func (m *MultiChoice) Cleanup(rl *readline.Instance, val interface{}) error { // execute the output summary template with the answer output, err := core.RunTemplate( - MultiChoiceQuestionTemplate, - MultiChoiceTemplateData{ - MultiChoice: *m, + MultiSelectQuestionTemplate, + MultiSelectTemplateData{ + MultiSelect: *m, SelectedIndex: m.selectedIndex, Checked: m.checked, Answer: strings.Join(val.([]string), ","), diff --git a/multichoice_test.go b/multiselect_test.go similarity index 76% rename from multichoice_test.go rename to multiselect_test.go index 56e363a8..4aa190b3 100644 --- a/multichoice_test.go +++ b/multiselect_test.go @@ -12,17 +12,17 @@ func init() { core.DisableColor = true } -func TestCanFormatMultiChoiceOptions(t *testing.T) { +func TestCanFormatMultiSelectOptions(t *testing.T) { - prompt := &MultiChoice{ + prompt := &MultiSelect{ Options: []string{"foo", "bar", "baz", "buz"}, Defaults: []string{"bar", "buz"}, } actual, err := core.RunTemplate( - MultiChoiceOptionsTemplate, - MultiChoiceTemplateData{ - MultiChoice: *prompt, + MultiSelectOptionsTemplate, + MultiSelectTemplateData{ + MultiSelect: *prompt, SelectedIndex: 2, Checked: map[int]bool{1: true, 3: true}, }, @@ -43,17 +43,17 @@ func TestCanFormatMultiChoiceOptions(t *testing.T) { } } -func TestMultiChoiceFormatQuestion(t *testing.T) { +func TestMultiSelectFormatQuestion(t *testing.T) { - prompt := &MultiChoice{ + prompt := &MultiSelect{ Message: "Pick your words:", Options: []string{"foo", "bar", "baz", "buz"}, Defaults: []string{"bar", "buz"}, } actual, err := core.RunTemplate( - MultiChoiceQuestionTemplate, - MultiChoiceTemplateData{MultiChoice: *prompt}, + MultiSelectQuestionTemplate, + MultiSelectTemplateData{MultiSelect: *prompt}, ) if err != nil { t.Errorf("Failed to run template to format checkbox question: %s", err) @@ -66,17 +66,17 @@ func TestMultiChoiceFormatQuestion(t *testing.T) { } } -func TestMultiChoiceFormatAnswer(t *testing.T) { +func TestMultiSelectFormatAnswer(t *testing.T) { - prompt := &MultiChoice{ + prompt := &MultiSelect{ Message: "Pick your words:", Options: []string{"foo", "bar", "baz", "buz"}, Defaults: []string{"bar", "buz"}, } actual, err := core.RunTemplate( - MultiChoiceQuestionTemplate, - MultiChoiceTemplateData{MultiChoice: *prompt, Answer: strings.Join([]string{"foo", "buz"}, ", ")}, + MultiSelectQuestionTemplate, + MultiSelectTemplateData{MultiSelect: *prompt, Answer: strings.Join([]string{"foo", "buz"}, ", ")}, ) if err != nil { t.Errorf("Failed to run template to format checkbox answer: %s", err) From 2b5a00e7039d2f5d689653fbfef2aa56382bc6f0 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 20:08:27 -0700 Subject: [PATCH 26/42] removed internal answer fields --- confirm.go | 1 - multiselect.go | 1 - 2 files changed, 2 deletions(-) diff --git a/confirm.go b/confirm.go index 8bdf50a0..012948fe 100644 --- a/confirm.go +++ b/confirm.go @@ -13,7 +13,6 @@ import ( type Confirm struct { Message string Default bool - Answer *bool } // data available to the templates when processing diff --git a/multiselect.go b/multiselect.go index ebdf9bf2..dc5af81e 100644 --- a/multiselect.go +++ b/multiselect.go @@ -16,7 +16,6 @@ type MultiSelect struct { Message string Options []string Defaults []string - Answer *[]string selectedIndex int checked map[int]bool } From e5f12dd5c0856ffe8e41e7aa718bfdb78c063664 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 21:08:45 -0700 Subject: [PATCH 27/42] simple example uses new api --- confirm.go | 7 ------- examples/simple.go | 8 ++++++-- select.go | 25 +++++++++++++------------ select_test.go | 4 ++-- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/confirm.go b/confirm.go index 012948fe..54660a75 100644 --- a/confirm.go +++ b/confirm.go @@ -89,13 +89,6 @@ func (c *Confirm) getBool(rl *readline.Instance) (bool, error) { // Prompt prompts the user with a simple text field and expects a reply followed // by a carriage return. func (c *Confirm) Prompt(rl *readline.Instance) (interface{}, error) { - // if we weren't passed an answer - if c.Answer == nil { - // build one - answer := false - c.Answer = &answer - } - // render the question template out, err := core.RunTemplate( ConfirmQuestionTemplate, diff --git a/examples/simple.go b/examples/simple.go index d9db35e0..e8731266 100644 --- a/examples/simple.go +++ b/examples/simple.go @@ -26,13 +26,17 @@ var simpleQs = []*survey.Question{ } func main() { + answers := struct { + Name string + Color string + }{} // ask the question - answers, err := survey.Ask(simpleQs) + err := survey.Ask(simpleQs, &answers) if err != nil { fmt.Println(err.Error()) return } // print the answers - fmt.Printf("%s chose %s", answers["name"], answers["color"]) + fmt.Printf("%s chose %s.\n", answers.Name, answers.Color) } diff --git a/select.go b/select.go index fc66db33..609dfab0 100644 --- a/select.go +++ b/select.go @@ -16,13 +16,14 @@ type Select struct { Message string Options []string Default string - SelectedIndex int + selectedIndex int } // the data available to the templates when processing type SelectTemplateData struct { Select - Answer string + SelectedIndex int + Answer string } const ( @@ -33,7 +34,7 @@ const ( // the template used to show the list of Selects SelectChoicesTemplate = ` {{- range $ix, $choice := .Options}} - {{- if eq $ix $.Select.SelectedIndex}}{{color "cyan+b"}}> {{else}}{{color "default+hb"}} {{end}} + {{- if eq $ix $.SelectedIndex}}{{color "cyan+b"}}> {{else}}{{color "default+hb"}} {{end}} {{- $choice}} {{- color "reset"}} {{end}}` @@ -43,22 +44,22 @@ const ( func (s *Select) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) { // if the user pressed the enter key if key == terminal.KeyEnter { - return []rune(s.Options[s.SelectedIndex]), 0, true + return []rune(s.Options[s.selectedIndex]), 0, true // if the user pressed the up arrow - } else if key == terminal.KeyArrowUp && s.SelectedIndex > 0 { + } else if key == terminal.KeyArrowUp && s.selectedIndex > 0 { // decrement the selected index - s.SelectedIndex-- + s.selectedIndex-- // if the user pressed down and there is room to move - } else if key == terminal.KeyArrowDown && s.SelectedIndex < len(s.Options)-1 { + } else if key == terminal.KeyArrowDown && s.selectedIndex < len(s.Options)-1 { // increment the selected index - s.SelectedIndex++ + s.selectedIndex++ } // render the options s.render() // if we are not pressing ent - return []rune(s.Options[s.SelectedIndex]), 0, true + return []rune(s.Options[s.selectedIndex]), 0, true } func (s *Select) render() error { @@ -70,7 +71,7 @@ func (s *Select) render() error { // the formatted response out, err := core.RunTemplate( SelectChoicesTemplate, - SelectTemplateData{Select: *s}, + SelectTemplateData{Select: *s, SelectedIndex: s.selectedIndex}, ) if err != nil { return err @@ -111,12 +112,12 @@ func (s *Select) Prompt(rl *readline.Instance) (interface{}, error) { } } // save the selected index - s.SelectedIndex = sel + s.selectedIndex = sel // render the initial question out, err := core.RunTemplate( SelectQuestionTemplate, - SelectTemplateData{Select: *s}, + SelectTemplateData{Select: *s, SelectedIndex: sel}, ) if err != nil { return "", err diff --git a/select_test.go b/select_test.go index c6b83f6b..0153bfeb 100644 --- a/select_test.go +++ b/select_test.go @@ -18,11 +18,11 @@ func TestCanFormatSelectOptions(t *testing.T) { Default: "baz", } // TODO: figure out a way for the test to actually test this bit of code - prompt.SelectedIndex = 2 + prompt.selectedIndex = 2 actual, err := core.RunTemplate( SelectChoicesTemplate, - SelectTemplateData{Select: *prompt}, + SelectTemplateData{Select: *prompt, SelectedIndex: 2}, ) if err != nil { From b2abdb2fcbca9abf11c216876eba6bdf154d2aab Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 21:31:08 -0700 Subject: [PATCH 28/42] cleanup --- README.md | 10 ++++++++-- validate_test.go | 4 +--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d0332de6..aa807a26 100644 --- a/README.md +++ b/README.md @@ -35,14 +35,20 @@ var qs = []*survey.Question{ } func main() { - answers, err := survey.Ask(qs) + // the answers will be written to this struct + answers := struct { + Name string + Color string + } + + err := survey.Ask(qs, &answers) if err != nil { fmt.Println("\n", err.Error()) return } - fmt.Printf("%s chose %s.", answers["name"], answers["color"]) + fmt.Printf("%s chose %s.", answers.Name, answers.Color) } ``` diff --git a/validate_test.go b/validate_test.go index 9b19500d..601363c0 100644 --- a/validate_test.go +++ b/validate_test.go @@ -44,10 +44,8 @@ func TestMaxLength(t *testing.T) { } func TestMinLength(t *testing.T) { - // the string to test - testStr := randString(10) // validate the string - if err := MinLength(12)(testStr); err == nil { + if err := MinLength(12)(randString(10)); err == nil { t.Error("No error returned with input less than 12 characters.") } } From ba1f44715c84e559a8b4acebc930f0b5e06242a5 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 21:36:39 -0700 Subject: [PATCH 29/42] added test for confirm --- tests/confirm.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/confirm.go diff --git a/tests/confirm.go b/tests/confirm.go new file mode 100644 index 00000000..b2f0525f --- /dev/null +++ b/tests/confirm.go @@ -0,0 +1,37 @@ +package main + +import ( + "github.com/alecaivazis/survey" + "github.com/alecaivazis/survey/tests/util" +) + +var answer = false + +var goodTable = []TestUtil.TestTableEntry{ + { + "Enter 'yes'", &survey.Confirm{ + Message: "yes:", + }, &answer, + }, + { + "Enter 'no'", &survey.Confirm{ + Message: "yes:", + }, &answer, + }, + { + "default", &survey.Confirm{ + Message: "yes:", + Default: true, + }, &answer, + }, + { + "not recognized (enter random letter)", &survey.Confirm{ + Message: "yes:", + Default: true, + }, &answer, + }, +} + +func main() { + TestUtil.RunTable(goodTable) +} From 11d33181684bf81bb89b21e8c5adda9143d27a95 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 21:40:14 -0700 Subject: [PATCH 30/42] added test for multiselect --- tests/multiselect.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/multiselect.go diff --git a/tests/multiselect.go b/tests/multiselect.go new file mode 100644 index 00000000..9b4763fa --- /dev/null +++ b/tests/multiselect.go @@ -0,0 +1,35 @@ +package main + +import ( + "github.com/alecaivazis/survey" + "github.com/alecaivazis/survey/tests/util" +) + +var answer = []string{} + +var table = []TestUtil.TestTableEntry{ + { + "standard", &survey.MultiSelect{ + Message: "What days do you prefer:", + Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, + }, &answer, + }, + { + "default (sunday, tuesday)", &survey.MultiSelect{ + Message: "What days do you prefer:", + Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, + Defaults: []string{"Sunday", "Tuesday"}, + }, &answer, + }, + { + "default not found", &survey.MultiSelect{ + Message: "What days do you prefer:", + Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, + Defaults: []string{"Sundayaa"}, + }, &answer, + }, +} + +func main() { + TestUtil.RunTable(table) +} From 9030982352c412c9b104cf0940a4edf63924f512 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 21:40:54 -0700 Subject: [PATCH 31/42] removed confirm and multiselect examples --- examples/confirm.go | 22 ---------------------- examples/multiselect.go | 25 ------------------------- 2 files changed, 47 deletions(-) delete mode 100644 examples/confirm.go delete mode 100644 examples/multiselect.go diff --git a/examples/confirm.go b/examples/confirm.go deleted file mode 100644 index c38e46c7..00000000 --- a/examples/confirm.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/alecaivazis/survey" -) - -func main() { - prompt := &survey.Confirm{ - Message: "Are you happy?", - } - ans := false - err := survey.AskOne(prompt, &ans, nil) - - if err != nil { - fmt.Println(err.Error()) - return - } - - fmt.Printf("response: %v\n", ans) -} diff --git a/examples/multiselect.go b/examples/multiselect.go deleted file mode 100644 index 8bba406a..00000000 --- a/examples/multiselect.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/alecaivazis/survey" -) - -func main() { - days := []string{} - prompt := &survey.MultiSelect{ - Message: "What days do you prefer:", - Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - Defaults: []string{"Saturday", "Sunday"}, - } - - err := survey.AskOne(prompt, &days, nil) - - if err != nil { - fmt.Println(err.Error()) - return - } - - fmt.Printf("response: %s\n", days) -} From 6d304c957b3a81166db2bf5a9c00b3fd1d8e1069 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Sun, 16 Apr 2017 21:42:45 -0700 Subject: [PATCH 32/42] renamed multiselect default to Default --- multiselect.go | 6 +++--- multiselect_test.go | 16 ++++++++-------- tests/multiselect.go | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/multiselect.go b/multiselect.go index dc5af81e..c5ac0553 100644 --- a/multiselect.go +++ b/multiselect.go @@ -15,7 +15,7 @@ import ( type MultiSelect struct { Message string Options []string - Defaults []string + Default []string selectedIndex int checked map[int]bool } @@ -109,8 +109,8 @@ func (m *MultiSelect) Prompt(rl *readline.Instance) (interface{}, error) { // compute the default state m.checked = make(map[int]bool) // if there is a default - if len(m.Defaults) > 0 { - for _, dflt := range m.Defaults { + if len(m.Default) > 0 { + for _, dflt := range m.Default { for i, opt := range m.Options { // if the option correponds to the default if opt == dflt { diff --git a/multiselect_test.go b/multiselect_test.go index 4aa190b3..3ceb7da0 100644 --- a/multiselect_test.go +++ b/multiselect_test.go @@ -15,8 +15,8 @@ func init() { func TestCanFormatMultiSelectOptions(t *testing.T) { prompt := &MultiSelect{ - Options: []string{"foo", "bar", "baz", "buz"}, - Defaults: []string{"bar", "buz"}, + Options: []string{"foo", "bar", "baz", "buz"}, + Default: []string{"bar", "buz"}, } actual, err := core.RunTemplate( @@ -46,9 +46,9 @@ func TestCanFormatMultiSelectOptions(t *testing.T) { func TestMultiSelectFormatQuestion(t *testing.T) { prompt := &MultiSelect{ - Message: "Pick your words:", - Options: []string{"foo", "bar", "baz", "buz"}, - Defaults: []string{"bar", "buz"}, + Message: "Pick your words:", + Options: []string{"foo", "bar", "baz", "buz"}, + Default: []string{"bar", "buz"}, } actual, err := core.RunTemplate( @@ -69,9 +69,9 @@ func TestMultiSelectFormatQuestion(t *testing.T) { func TestMultiSelectFormatAnswer(t *testing.T) { prompt := &MultiSelect{ - Message: "Pick your words:", - Options: []string{"foo", "bar", "baz", "buz"}, - Defaults: []string{"bar", "buz"}, + Message: "Pick your words:", + Options: []string{"foo", "bar", "baz", "buz"}, + Default: []string{"bar", "buz"}, } actual, err := core.RunTemplate( diff --git a/tests/multiselect.go b/tests/multiselect.go index 9b4763fa..3e72350b 100644 --- a/tests/multiselect.go +++ b/tests/multiselect.go @@ -16,16 +16,16 @@ var table = []TestUtil.TestTableEntry{ }, { "default (sunday, tuesday)", &survey.MultiSelect{ - Message: "What days do you prefer:", - Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - Defaults: []string{"Sunday", "Tuesday"}, + Message: "What days do you prefer:", + Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, + Default: []string{"Sunday", "Tuesday"}, }, &answer, }, { "default not found", &survey.MultiSelect{ - Message: "What days do you prefer:", - Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, - Defaults: []string{"Sundayaa"}, + Message: "What days do you prefer:", + Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, + Default: []string{"Sundayaa"}, }, &answer, }, } From fc31fe17a86d62772738fd35016878e1e173cfa8 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Mon, 17 Apr 2017 01:15:18 -0700 Subject: [PATCH 33/42] added better prompt documentation --- README.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index aa807a26..1aa0d126 100644 --- a/README.md +++ b/README.md @@ -37,18 +37,89 @@ var qs = []*survey.Question{ func main() { // the answers will be written to this struct answers := struct { - Name string - Color string + Name string // survey will match the question and field names + FavoriteColor string `survey:"color"` // or you can tag fields to match a specific name } + // perform the questions err := survey.Ask(qs, &answers) - if err != nil { - fmt.Println("\n", err.Error()) + fmt.Println(err.Error()) return } - fmt.Printf("%s chose %s.", answers.Name, answers.Color) + fmt.Printf("%s chose %s.", answers.Name, answers.FavoriteColor) +} +``` + +## Examples +Examples can be found in the `examples/` directory. Run them +to see basic behavior: +```bash +go get github.com/alecaivazis/survey + +# ... navigate to the repo in your GOPATH + +go run examples/simple.go +go run examples/validation.go +``` + +## Prompts + +### Input + + +```golang +name := "" +prompt = &survey.Input{ + Message: "What's your name?", +} +survey.AskOne(prompt, &name, nil) +``` + + +### Password + + +```golang +password := "" +prompt = &survey.Password{"Please type your password"} +survey.AskOne(prompt, &password, nil) +``` + + +### Confirm + + +```golang +name := false +prompt = &survey.Confirm{ + Message: "Do you like pie?", +} +survey.AskOne(prompt, &name, nil) +``` + + +### Select + + +```golang +color := "" +prompt = &survey.Select{ + Options: []string{"red", "blue" "green"} } +survey.AskOne(prompt, &color, nil) +``` + +### MultiSelect + + +```golang +days := []string{} +prompt = &survey.MultiSelect{ + Message: "What days do you prefer:", + Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, +} +survey.AskOne(prompt, &days, nil) ``` From c3872f763bcbcef13394e90a289668ecd8714852 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Mon, 17 Apr 2017 01:15:24 -0700 Subject: [PATCH 34/42] made output prettier --- confirm.go | 4 ++-- multiselect.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/confirm.go b/confirm.go index 54660a75..fdc3222f 100644 --- a/confirm.go +++ b/confirm.go @@ -124,9 +124,9 @@ func (c *Confirm) Cleanup(rl *readline.Instance, val interface{}) error { ans := "" // if the value was previously true if val.(bool) { - ans = "true" + ans = "yes" } else { - ans = "false" + ans = "no" } // render the template diff --git a/multiselect.go b/multiselect.go index c5ac0553..cb9f81da 100644 --- a/multiselect.go +++ b/multiselect.go @@ -183,7 +183,7 @@ func (m *MultiSelect) Cleanup(rl *readline.Instance, val interface{}) error { MultiSelect: *m, SelectedIndex: m.selectedIndex, Checked: m.checked, - Answer: strings.Join(val.([]string), ","), + Answer: strings.Join(val.([]string), ", "), }, ) if err != nil { From 342fa25b327fc4e580b1b04c5c0fba90a59e3578 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Mon, 17 Apr 2017 01:18:08 -0700 Subject: [PATCH 35/42] added full giphy link to multi select screencast --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1aa0d126..fd6ff23c 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ survey.AskOne(prompt, &color, nil) ### MultiSelect - + ```golang days := []string{} From 481894deabd59e58e051cab7434b9cebbb8cc68e Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Mon, 17 Apr 2017 10:26:02 -0700 Subject: [PATCH 36/42] added section in readme for validators and package versioning --- README.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fd6ff23c..7254e032 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ go run examples/validation.go ```golang name := "" prompt = &survey.Input{ - Message: "What's your name?", + Message: "ping", } survey.AskOne(prompt, &name, nil) ``` @@ -123,3 +123,35 @@ prompt = &survey.MultiSelect{ } survey.AskOne(prompt, &days, nil) ``` + +## Validation + +Validating individual responses for a particular question can be done by defining a +`Validate` field on the `survey.Question` to be validated. This function takes an +`interface{}` type and returns an error to show to the user, prompting them for another +response: + +```golang +q := &survey.Question{ + Prompt: &survey.Input{Message: "Hello world validation"}, + Validate: func (val interface{}) error { + // since we are validating an Input, this will always succeed + if str, ok := val.(string) ; ok { + if len(str) > 10 { + return errors.New("This response cannot be longer than 10 characters.") + } + } + } +} +``` + +### Built-in Validators +surey comes prepackaged with a few validators to fit common situations. Currently these validators +include: + +| name | valid types | description | +|-----------|-----------------|---------------------------------------------------------| +| Required | any | Rejects zero values of the response type | +| MinLength | string | Enforces that a response is at least the given length | + +## Versioning From 0a21ce19e2ddd81e877e3025805e71bebc566286 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Mon, 17 Apr 2017 10:27:45 -0700 Subject: [PATCH 37/42] fixed a few typos in source examples --- README.md | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7254e032..101c4dd3 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,9 @@ survey.AskOne(prompt, &name, nil) ```golang password := "" -prompt = &survey.Password{"Please type your password"} +prompt = &survey.Password{ + Message: "Please type your password", +} survey.AskOne(prompt, &password, nil) ``` @@ -106,7 +108,8 @@ survey.AskOne(prompt, &name, nil) ```golang color := "" prompt = &survey.Select{ - Options: []string{"red", "blue" "green"} + Message: "Choose a color:", + Options: []string{"red", "blue" "green"}, } survey.AskOne(prompt, &color, nil) ``` @@ -146,12 +149,23 @@ q := &survey.Question{ ``` ### Built-in Validators -surey comes prepackaged with a few validators to fit common situations. Currently these validators -include: +`survey` comes prepackaged with a few validators to fit common situations. Currently these +validators include: -| name | valid types | description | -|-----------|-----------------|---------------------------------------------------------| -| Required | any | Rejects zero values of the response type | -| MinLength | string | Enforces that a response is at least the given length | +| name | valid types | description | +|--------------|-----------------|---------------------------------------------------------------| +| Required | any | Rejects zero values of the response type | +| MinLength(n) | string | Enforces that a response is at least the given length | +| MaxLength(n) | string | Enforces that a response is no longer than the given length | ## Versioning + +This project tries to maintain semantic GitHub releases as closely as possible. As such, services +like [gopkg.in](http://labix.org/gopkg.in) work very well to ensure non-breaking changes whenever +you build your application. For example, importing v1 of survey would look something like + +```golang +package main + +import "gopkg.in/alecaivazis/survey.v1" +``` From 4badc86ea195323d3ef9892298bc4bd1818e9b0e Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Mon, 17 Apr 2017 10:28:41 -0700 Subject: [PATCH 38/42] more readme typos --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 101c4dd3..99395b4e 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ func main() { answers := struct { Name string // survey will match the question and field names FavoriteColor string `survey:"color"` // or you can tag fields to match a specific name - } + }{} // perform the questions err := survey.Ask(qs, &answers) From 299cb1d50ffe94736999cccaedd20ccd85810ede Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Mon, 17 Apr 2017 21:40:05 -0700 Subject: [PATCH 39/42] fixed typos in prompt examples --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 99395b4e..a14676eb 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ go run examples/validation.go ```golang name := "" -prompt = &survey.Input{ +prompt := &survey.Input{ Message: "ping", } survey.AskOne(prompt, &name, nil) @@ -83,7 +83,7 @@ survey.AskOne(prompt, &name, nil) ```golang password := "" -prompt = &survey.Password{ +prompt := &survey.Password{ Message: "Please type your password", } survey.AskOne(prompt, &password, nil) @@ -95,7 +95,7 @@ survey.AskOne(prompt, &password, nil) ```golang name := false -prompt = &survey.Confirm{ +prompt := &survey.Confirm{ Message: "Do you like pie?", } survey.AskOne(prompt, &name, nil) @@ -107,9 +107,9 @@ survey.AskOne(prompt, &name, nil) ```golang color := "" -prompt = &survey.Select{ +prompt := &survey.Select{ Message: "Choose a color:", - Options: []string{"red", "blue" "green"}, + Options: []string{"red", "blue", "green"}, } survey.AskOne(prompt, &color, nil) ``` @@ -120,7 +120,7 @@ survey.AskOne(prompt, &color, nil) ```golang days := []string{} -prompt = &survey.MultiSelect{ +prompt := &survey.MultiSelect{ Message: "What days do you prefer:", Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, } From 0936fbbcab32e76643e077d20b37bd1241ac41e7 Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Mon, 17 Apr 2017 21:46:46 -0700 Subject: [PATCH 40/42] updated prompt examples --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a14676eb..f70c2978 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ survey.AskOne(prompt, &name, nil) ### Password - + ```golang password := "" @@ -91,7 +91,7 @@ survey.AskOne(prompt, &password, nil) ### Confirm - + ```golang name := false @@ -103,7 +103,7 @@ survey.AskOne(prompt, &name, nil) ### Select - + ```golang color := "" @@ -116,7 +116,7 @@ survey.AskOne(prompt, &color, nil) ### MultiSelect - + ```golang days := []string{} From 8c82316a88668045fc825b713ee8668a0ddc362e Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Mon, 17 Apr 2017 21:48:49 -0700 Subject: [PATCH 41/42] increased size of prompt examples --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f70c2978..207b5148 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ go run examples/validation.go ## Prompts ### Input - + ```golang name := "" @@ -79,7 +79,7 @@ survey.AskOne(prompt, &name, nil) ### Password - + ```golang password := "" @@ -91,7 +91,7 @@ survey.AskOne(prompt, &password, nil) ### Confirm - + ```golang name := false @@ -103,7 +103,7 @@ survey.AskOne(prompt, &name, nil) ### Select - + ```golang color := "" @@ -116,7 +116,7 @@ survey.AskOne(prompt, &color, nil) ### MultiSelect - + ```golang days := []string{} From 46e65ef8ab5098f79fe6e99e65f7f215bc86334d Mon Sep 17 00:00:00 2001 From: alec aivazis Date: Mon, 17 Apr 2017 22:50:44 -0700 Subject: [PATCH 42/42] fixed casing errors in imports --- .tasks.yml | 2 +- .travis.yml | 2 +- README.md | 8 ++++---- confirm.go | 4 ++-- confirm_test.go | 2 +- core/write.go | 2 +- examples/simple.go | 2 +- examples/validation.go | 2 +- input.go | 4 ++-- input_test.go | 2 +- multiselect.go | 4 ++-- multiselect_test.go | 2 +- password.go | 2 +- password_test.go | 2 +- select.go | 4 ++-- select_test.go | 2 +- survey.go | 4 ++-- survey_test.go | 2 +- tests/ask.go | 2 +- tests/confirm.go | 4 ++-- tests/input.go | 4 ++-- tests/multiselect.go | 4 ++-- tests/password.go | 4 ++-- tests/select.go | 4 ++-- tests/util/test.go | 2 +- 25 files changed, 38 insertions(+), 38 deletions(-) diff --git a/.tasks.yml b/.tasks.yml index 1dd5ea20..d87975aa 100644 --- a/.tasks.yml +++ b/.tasks.yml @@ -7,4 +7,4 @@ install-deps: command: go get -t {{.files}} variables: - files: '$(go list -v ./... | grep -Ev "github.com/alecaivazis/survey/(tests|examples)")' + files: '$(go list -v ./... | grep -Ev "github.com/AlecAivazis/survey/(tests|examples)")' diff --git a/.travis.yml b/.travis.yml index 114ee0c2..bd8c87bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go before_install: - - go get github.com/alecaivazis/run + - go get github.com/AlecAivazis/run install: - run install-deps diff --git a/README.md b/README.md index 207b5148..a66caa32 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Survey [![Build Status](https://travis-ci.org/AlecAivazis/survey.svg?branch=feature%2Fpretty)](https://travis-ci.org/AlecAivazis/survey) -[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg)](https://godoc.org/github.com/alecaivazis/survey) +[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg)](https://godoc.org/github.com/AlecAivazis/survey) A library for building interactive prompts. Heavily inspired by the great [inquirer.js](https://github.com/SBoudrias/Inquirer.js/). @@ -14,7 +14,7 @@ package main import ( "fmt" - "gopkg.in/alecaivazis/survey.v1" + "gopkg.in/AlecAivazis/survey.v1" ) // the questions to ask @@ -56,7 +56,7 @@ func main() { Examples can be found in the `examples/` directory. Run them to see basic behavior: ```bash -go get github.com/alecaivazis/survey +go get github.com/AlecAivazis/survey # ... navigate to the repo in your GOPATH @@ -167,5 +167,5 @@ you build your application. For example, importing v1 of survey would look somet ```golang package main -import "gopkg.in/alecaivazis/survey.v1" +import "gopkg.in/AlecAivazis/survey.v1" ``` diff --git a/confirm.go b/confirm.go index fdc3222f..786b4898 100644 --- a/confirm.go +++ b/confirm.go @@ -4,8 +4,8 @@ import ( "fmt" "regexp" - "github.com/alecaivazis/survey/core" - "github.com/alecaivazis/survey/terminal" + "github.com/AlecAivazis/survey/core" + "github.com/AlecAivazis/survey/terminal" "github.com/chzyer/readline" ) diff --git a/confirm_test.go b/confirm_test.go index feaf15f3..c117d0bf 100644 --- a/confirm_test.go +++ b/confirm_test.go @@ -3,7 +3,7 @@ package survey import ( "testing" - "github.com/alecaivazis/survey/core" + "github.com/AlecAivazis/survey/core" ) func init() { diff --git a/core/write.go b/core/write.go index a44722e8..6c3c862e 100644 --- a/core/write.go +++ b/core/write.go @@ -43,7 +43,7 @@ func WriteAnswer(t interface{}, name string, v interface{}) (err error) { return copy(elem, value) } -// BUG(alecaivazis): the current implementation might cause weird conflicts if there are +// BUG(AlecAivazis): the current implementation might cause weird conflicts if there are // two fields with same name that only differ by casing. func findFieldIndex(s reflect.Value, name string) (int, error) { // the type of the value diff --git a/examples/simple.go b/examples/simple.go index e8731266..c3723fa5 100644 --- a/examples/simple.go +++ b/examples/simple.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "github.com/alecaivazis/survey" + "github.com/AlecAivazis/survey" ) // the questions to ask diff --git a/examples/validation.go b/examples/validation.go index 998cd9c6..396aa0cb 100644 --- a/examples/validation.go +++ b/examples/validation.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "github.com/alecaivazis/survey" + "github.com/AlecAivazis/survey" ) // the questions to ask diff --git a/input.go b/input.go index bd85de79..3ca884db 100644 --- a/input.go +++ b/input.go @@ -3,8 +3,8 @@ package survey import ( "fmt" - "github.com/alecaivazis/survey/core" - "github.com/alecaivazis/survey/terminal" + "github.com/AlecAivazis/survey/core" + "github.com/AlecAivazis/survey/terminal" "github.com/chzyer/readline" ) diff --git a/input_test.go b/input_test.go index 5b5d3edf..32e372f7 100644 --- a/input_test.go +++ b/input_test.go @@ -3,7 +3,7 @@ package survey import ( "testing" - "github.com/alecaivazis/survey/core" + "github.com/AlecAivazis/survey/core" ) func init() { diff --git a/multiselect.go b/multiselect.go index cb9f81da..7a0ff0e4 100644 --- a/multiselect.go +++ b/multiselect.go @@ -5,8 +5,8 @@ import ( "io/ioutil" "strings" - "github.com/alecaivazis/survey/core" - "github.com/alecaivazis/survey/terminal" + "github.com/AlecAivazis/survey/core" + "github.com/AlecAivazis/survey/terminal" "github.com/chzyer/readline" ) diff --git a/multiselect_test.go b/multiselect_test.go index 3ceb7da0..ce53a510 100644 --- a/multiselect_test.go +++ b/multiselect_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/alecaivazis/survey/core" + "github.com/AlecAivazis/survey/core" ) func init() { diff --git a/password.go b/password.go index c4619b22..08531aa5 100644 --- a/password.go +++ b/password.go @@ -3,7 +3,7 @@ package survey import ( "github.com/chzyer/readline" - "github.com/alecaivazis/survey/core" + "github.com/AlecAivazis/survey/core" ) // Password is like a normal Input but the text shows up as *'s and diff --git a/password_test.go b/password_test.go index 6a871048..3d1d049d 100644 --- a/password_test.go +++ b/password_test.go @@ -3,7 +3,7 @@ package survey import ( "testing" - "github.com/alecaivazis/survey/core" + "github.com/AlecAivazis/survey/core" ) func init() { diff --git a/select.go b/select.go index 609dfab0..109a9df7 100644 --- a/select.go +++ b/select.go @@ -5,8 +5,8 @@ import ( "io/ioutil" "strings" - "github.com/alecaivazis/survey/core" - "github.com/alecaivazis/survey/terminal" + "github.com/AlecAivazis/survey/core" + "github.com/AlecAivazis/survey/terminal" "github.com/chzyer/readline" ) diff --git a/select_test.go b/select_test.go index 0153bfeb..861f0045 100644 --- a/select_test.go +++ b/select_test.go @@ -3,7 +3,7 @@ package survey import ( "testing" - "github.com/alecaivazis/survey/core" + "github.com/AlecAivazis/survey/core" ) func init() { diff --git a/survey.go b/survey.go index 62a53057..6639176c 100644 --- a/survey.go +++ b/survey.go @@ -4,8 +4,8 @@ import ( "errors" "fmt" - "github.com/alecaivazis/survey/core" - "github.com/alecaivazis/survey/terminal" + "github.com/AlecAivazis/survey/core" + "github.com/AlecAivazis/survey/terminal" "github.com/chzyer/readline" ) diff --git a/survey_test.go b/survey_test.go index e457fa22..e59b51f4 100644 --- a/survey_test.go +++ b/survey_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/alecaivazis/survey/core" + "github.com/AlecAivazis/survey/core" ) func init() { diff --git a/tests/ask.go b/tests/ask.go index ea6dcf41..dcd74d0e 100644 --- a/tests/ask.go +++ b/tests/ask.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "github.com/alecaivazis/survey" + "github.com/AlecAivazis/survey" ) // the questions to ask diff --git a/tests/confirm.go b/tests/confirm.go index b2f0525f..386b0a57 100644 --- a/tests/confirm.go +++ b/tests/confirm.go @@ -1,8 +1,8 @@ package main import ( - "github.com/alecaivazis/survey" - "github.com/alecaivazis/survey/tests/util" + "github.com/AlecAivazis/survey" + "github.com/AlecAivazis/survey/tests/util" ) var answer = false diff --git a/tests/input.go b/tests/input.go index ebbe6ec1..4c76189b 100644 --- a/tests/input.go +++ b/tests/input.go @@ -1,8 +1,8 @@ package main import ( - "github.com/alecaivazis/survey" - "github.com/alecaivazis/survey/tests/util" + "github.com/AlecAivazis/survey" + "github.com/AlecAivazis/survey/tests/util" ) var val = "" diff --git a/tests/multiselect.go b/tests/multiselect.go index 3e72350b..8d2d6926 100644 --- a/tests/multiselect.go +++ b/tests/multiselect.go @@ -1,8 +1,8 @@ package main import ( - "github.com/alecaivazis/survey" - "github.com/alecaivazis/survey/tests/util" + "github.com/AlecAivazis/survey" + "github.com/AlecAivazis/survey/tests/util" ) var answer = []string{} diff --git a/tests/password.go b/tests/password.go index dc2c7b27..e37b293b 100644 --- a/tests/password.go +++ b/tests/password.go @@ -1,8 +1,8 @@ package main import ( - "github.com/alecaivazis/survey" - "github.com/alecaivazis/survey/tests/util" + "github.com/AlecAivazis/survey" + "github.com/AlecAivazis/survey/tests/util" ) var value = "" diff --git a/tests/select.go b/tests/select.go index e9bea9b6..7a84a1eb 100644 --- a/tests/select.go +++ b/tests/select.go @@ -1,8 +1,8 @@ package main import ( - "github.com/alecaivazis/survey" - "github.com/alecaivazis/survey/tests/util" + "github.com/AlecAivazis/survey" + "github.com/AlecAivazis/survey/tests/util" ) var answer = "" diff --git a/tests/util/test.go b/tests/util/test.go index 15708dd6..5b490206 100644 --- a/tests/util/test.go +++ b/tests/util/test.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "github.com/alecaivazis/survey" + "github.com/AlecAivazis/survey" ) type TestTableEntry struct {