Closed weshicks closed 6 years ago
Hey @weshicks - thanks for submitting this!
So similar libraries use a field on their questions (usually called "when") to change the question list at runtime. However, most of the solutions I know of are not in statically typed languages so they can be nice one liners like
{
...
when: answers => answers.favoriteFood == tacos
}
However, since we have to find a statically typable solution, the only thing I can think of is that the when
field has a type signature of func (answers interface{}) bool
which forces a lot of awkward type assertions in the user's code. Also, since the ultimate set of questions can be a rather gnarly graph the logic inside of a when
can get prettty complicated. ie, if they chose pizza, and you asked them if they wanted tomatoes, you might want to ask them a third question like "fresh" or "diced" if they did want tomatoes - not a great example but hopefully you see what i'm saying.
Because of these 2 "problems", I haven't actually sat down to implement when
. There is an old issue
to capture this effort (#5), but every time I sit down to do it i end up coming to the same conclusion - while a little uglier, putting the branching logic in user land and calling survey.Ask
multiple times is ultimately cleaner and exposes fewer interface{}
s to the user which means it "feels" better imo.
Ah, yeah I'm tracking with you on the complications that introducing that sort of structure could impose! Thanks for laying it out like that, makes sense. I think I can spend some time reimagining the solution to the problem I'm trying to solve with this in mind.
I'm still new to Go, but maybe somewhere down the line I'll play around with this concept further. In the meantime, feel free to close this! Thanks for this tool, it's really handy!
Awesome - i'm definitely interested in solving this problem in survey so if you have a solution that addresses these i'm definitely interested in checking it out!
I'm happy to hear it's been working for you. Please feel free to reach out if you run into problems. You can find me on the gophers slack If I don't respond here or if you want something more direct
@AlecAivazis why do you think simple func() bool
won't work? This is basically all the code you need:
type Conditioner func() bool
type Question struct {
Name string
Prompt Prompt
Validate Validator
Transform Transformer
Condition Conditioner
}
func Ask(qs []*Question, response interface{}, opts ...AskOpt) error {
// ...
for _, q := range qs {
// Skip the question if the condition is not met
if q.Condition != nil && !q.Condition() {
continue
}
answers := make(map[string]interface{})
questions := []*Question{
{
Name: "q1",
Prompt: &Confirm{
Message: "q1",
},
},
{
Name: "q2",
Prompt: &Confirm{
Message: "q2",
},
Condition: func() bool {
return answers["q1"]
},
},
}
@AlecAivazis I ran into this issue today. This would be really useful. Not sure if @tetafro wants to submit a pr, but if not I can go ahead and submit one if that looks good.
The only thing I would add is passing response interface{}
to the provided Conditioner
function because the response object isn't always going to be in the same scope as the survey.Question
e.g.:
type Conditioner func(response interface{}) bool
...
for _, q := range qs {
// Skip the question if the condition is not met
if q.Condition != nil && !q.Condition(response) {
continue
}
It’s not that I don’t think you’re suggestion would work @tetafro, I’m pretty sure it would! I just want to make sure that we don’t just jump onto one specific solution because it would work.
I’m not sure that the additional API you suggested actually simplifies the overall logic that would be required if you split up the set of questions into two. As such, I’m not sure I think we should complicate the API to simplify the easy cases without it also helping the complicated ones
split up the set of questions into two
@AlecAivazis quite simple, I agree. But I want to just write array of questions and run it :) I don't think that having basic tools for managing questions would be unnecessary API complication.
without it also helping the complicated ones
Can you provide an example of complicated case?
This is more of a question than a bug, but it might be a feature request. I did some digging, and I wasn't able to find anyone else asking for / about this so here goes.
Use case: I'm building a CLI using survey to handle questions / responses, and would like to know the "best" way to go about having conditional questions, that is, questions that only appear based on your responses to other questions.
Say you have a MultiSelect question that asks a user what their favorite food is. If they select "Pizza", I'd like to ask them questions about their pizza preferences, if they answer "Tacos" I'd like to ask them questions about tacos, and so on.
Is this possible with Survey, and I've just missed it in the docs? If not, is something like this within the scope of what this project is looking to solve for? Or should I structure my CLI application to have several smaller sub-survey questions? Ideally I'd like one consistent user flow through the whole process.
Any thoughts/feedback would be appreciated!