felangel / mason

Tools which allow developers to create and consume reusable templates called bricks.
https://docs.brickhub.dev
989 stars 99 forks source link

fix: Declare brick.yaml variable as a list of strings #583

Open matthew-carroll opened 2 years ago

matthew-carroll commented 2 years ago

Description

I'd like to configure one of my brick variables to be a list of strings. I'd like to provide a default value for that list in brick.yaml.

vars:
  some_list_var:
    type: array
    description: Some description
    default: 
      - This is the default option
    prompt: What items do you want?

Expected Behavior

I'm prompted for a list of values, with the default set to what configuration in the brick.yaml file.

Actual behavior

I get an error when running mason.

Screenshots

Screen Shot 2022-10-28 at 10 15 31 PM In this case, the variable is named "copyright_holders"

felangel commented 2 years ago

Hi @matthew-carroll 👋 Thanks for opening an issue!

The array type expects you to provide a list of values from which the user can choose zero or more values:

vars:
  flavors:
    type: array
    description: The supported flavors.
    prompt: What flavors do you want?
    defaults: 
      - production
    values:
      - development
      - integration
      - production
? What flavors do you want?
❯ ◯  development
  ◯  integration
  ◉  production

If you want users to enter a free-form list then you can just use type string and prompt them to provide a list:

vars:
  flavors:
    type: string
    description: The supported flavors.
    prompt: What flavors do you want?
    default: '["development", "production"]'

Alternatively, you can also use type string and prompt them to enter a comma-separated list (for example) and you can parse the input and convert it to a list in a pre_gen.dart hook:

vars:
  flavors:
    type: string
    description: The supported flavors.
    prompt: What flavors do you want? (as a comma separated list)
    default: "development,production"
// pre_gen.dart
import 'package:mason/mason.dart';

void run(HookContext context) {
  // Manipulate the user's string input and convert it to a list
  context.vars['flavors'] = context.vars['flavors'].split(',');
}

Another option is to use a pre_gen.dart hook to continuously prompt for values and build up the list of selections while some condition is true. You can see an example of this used by brick:model. The relevant code is https://github.com/LukeMoody01/mason_bricks/blob/038394aced869a007eedcea81b6151bf484a6243/bricks/model/hooks/pre_gen.dart#L103.

Let me know if that helps!

matthew-carroll commented 2 years ago

In my case I'm interested in a list of free-form values.

Given that lists are supported for bounded sets, is there any reason not to support lists of free-form values? I would assume that such situations aren't uncommon, and registering programmatic hooks to support comma-separated lists of free-form values seems like a considerable amount of work, when multiplied across all the Mason users.

In fact, is there any particular philosophy about which filters, transforms, etc are included with Mason? I know there are a bunch of case-formatting lambdas, but I think those were all that were listed in the README.

felangel commented 2 years ago

In my case I'm interested in a list of free-form values.

Given that lists are supported for bounded sets, is there any reason not to support lists of free-form values? I would assume that such situations aren't uncommon, and registering programmatic hooks to support comma-separated lists of free-form values seems like a considerable amount of work, when multiplied across all the Mason users.

This is already possible without hooks if you're okay with users providing the input as a list:

vars:
  flavors:
    type: string
    description: The supported flavors.
    prompt: What flavors do you want?
    default: '["development", "production"]'

It all comes down to what you want the user experience to be. What would be your ideal API and resulting user experience? I'm more than happy to extend the existing API (maybe with a list type) but we just need to define what the API and resulting prompting experience would be.

In fact, is there any particular philosophy about which filters, transforms, etc are included with Mason? I know there are a bunch of case-formatting lambdas, but I think those were all that were listed in the README.

The philosophy has been to include lambdas for common transforms and leave it up to developers to handle one-off/less common use cases via hooks. You can find a complete list of supported lambdas in the docs.

matthew-carroll commented 2 years ago

It all comes down to what you want the user experience to be

I think there a couple obvious ways to collect a list of values.

First, let the user enter a comma-separated list, or items separated by spaces. A corresponding parser might allow for a little bit of variation here. But we know the user is trying to type a series of values:

What are the authors? Adam, Jill, Jane

Those items could then be parsed and provided to the template system as a list, like any other list. The separator could be configurable.

The second common approach would be to enter the items one-by-one. This takes any onus off the user to understand what the item separate should be, or to understand whether spaces are allowed after the separator, e.g. "," vs ", " vs ", ".

Who are the authors? Adam
  Any more? Jill
  Any more? Jane
  Any more? <submit empty>

The "Any more?" prompt could be configurable. You could also allow the prompt to be omitted and default to a command prompt "> ".

I think those modalities are likely to cover the majority of free-form list input. If those were supported, it would remove the parsing behavior that many developers would otherwise need replicate in each project, and it would provide an easy-to-understand prompt experience for brick users.

I'm also curious about the other side of the equation, which is assembling a list of strings in the template file. Continuing with this example, is there any easy way to achieve the following without implementing a hook?

**Authors:** Adam, Jill, Jane.
felangel commented 2 years ago

I think those modalities are likely to cover the majority of free-form list input. If those were supported, it would remove the parsing behavior that many developers would otherwise need replicate in each project, and it would provide an easy-to-understand prompt experience for brick users.

Just to clarify, you'd prefer to have support for both the single line entry with some configurable separator as well as a one-by-one entry? Are there specific scenarios in which you'd prefer the single line entry over the one-by-one entry? I'd love to see if we can come up with a unified approach.

I'm also curious about the other side of the equation, which is assembling a list of strings in the template file. Continuing with this example, is there any easy way to achieve the following without implementing a hook?

Authors: Adam, Jill, Jane.

At the moment, that exact formatting isn't possible without a hook due to limitations in mustache. I've been thinking about supporting utilities to make this possible see https://github.com/felangel/mason/issues/419 but definitely open to suggestions for how this can be improved, thanks!

matthew-carroll commented 2 years ago

Just to clarify, you'd prefer to have support for both the single line entry with some configurable separator as well as a one-by-one entry?

I'm personally happy with just the line-by-line entry.