App::Y...:Option(3pm)

πŸ’… App::Yath::Option(3pm) πŸ’…

okay bestie, welcome to the yath option bible, part one of three, buckle up πŸŽ€


✨ NAME ✨

App::Yath::Option β€” it's literally the representation of a yath option. that's the whole job. 🫢


🌸 DESCRIPTION 🌸

So this class πŸ“¦ represents one single command line option for yath. like, just one. one flag, one job, main character energy. no cap.


πŸ¦‹ SYNOPSIS πŸ¦‹

ngl you usually will not be creating option instances directly, bestie. like, why would you. normally you use App::Yath::Options which gives you sugar πŸ“ and makes sure your options actually land in the right place. iykyk.

use App::Yath::Options;

# You can specify a single option:
option color => (
    prefix      => 'display',
    category    => "Display Options",
    description => "Turn color on, default is true if STDOUT is a TTY.",
    default     => sub { -t STDOUT ? 1 : 0 },
);

# If you are specifying multiple options you can use an option_group to
# define common parameters.
option_group {prefix => 'display', category => "Display Options"} => sub {
    option color => (
        description => "Turn color on, default is true if STDOUT is a TTY.",
        default     => sub { -t STDOUT ? 1 : 0 },
    );

    option verbose => (
        short       => 'v',
        type        => 'c',
        description => "Be more verbose",
        default     => 0,
    );
};

πŸ‘‘ ATTRIBUTES πŸ‘‘

these can be handed over at object construction time, or they get generated internally by the machinery. either way, slay.

🧚 CONSTRUCTION ONLY

  • applicable => sub { ... } β€” this callback is what the applicable() method uses. you feed it a coderef and it decides if the option is even applicable. πŸ‘‡
option foo => (
    ...,
    applicable => sub {
        my ($opt, $options) = @_;
        ...
        return $bool;
    },
);

πŸ’– READ-ONLY

these ones you just read, you don't write them, periodt.

⭐ REQUIRED

  • $class->new(prefix => 'my_prefix')
  • $scalar = $opt->prefix()

a prefix is required, no exceptions bestie. every option has its value inserted into the settings structure, which is an instance of Test2::Harness::Settings. the shape is "$settings->PREFIX->OPTION".

if you don't give a "name" attribute, the default name ends up being "PREFIX-TITLE". and the name is the actual command line argument, so the default name means the flag is --PREFIX-TITLE. iconic.

  • $class->new(type => $type)
  • $type = $opt->type()

every option needs a type. if you don't say anything, the default is 'b', a.k.a. boolean.

here's every possible type, plus its aliases. you can use the type character or any of the aliases to say which one you want:

  • b bool boolean β€” true or false values, will be normalized to 0 or 1 in most cases.
  • c count counter counting β€” a counter, starts at 0 and then increments every single time the option is used. πŸ’―
  • s scalar string number β€” needs an argument, and that argument is treated as a scalar value. no type checking happens in the option itself, though you can check it yourself using "action" or "normalize" callbacks, which are documented under those attributes.
  • m multi multiple list array β€” needs an argument treated as a scalar value, and you can use it multiple times. all the arguments you hand over get appended to an array. πŸŽ€
  • d def default β€” argument is optional, scalar when it is provided. use --opt=arg to provide an argument β€” --opt arg will not work, arg gets seen as its own separate item on the command line. you can also use it with no arg at all (--opt) to signal that a default argument should be used (set via the "action" callback β€” not the "default" attribute, which is a default value regardless of whether the option is used at all).

real world example from the debug options (simplified for doc purposes, don't @ me):

option summary => (
    type        => 'd',
    description => "Write out a summary json file, if no path is provided 'summary.json' will be used. The .json extension is added automatically if omitted.",

    long_examples => ['', '=/path/to/summary.json'],

    # New way to specify an auto-fill value for when no =VAL is provided.
    # If you do not specify this the default autofill is '1' for legacy support.
    autofill => 'VALUE',

    # Old way to autofill a value (default is 1 for auto-fill)
    # Using autofill is significantly better.
    # You can also use action for additional behavior along with autofill,
    # but the default will be your auto-fill value, not '1'.
    action => sub {
        my ($prefix, $field, $raw, $norm, $slot, $settings) = @_;

        # $norm will be '1' if option was used without an argument, so we
        # just use the provided value when it is not 1'.
        return $$slot = $norm unless $norm eq '1';

        # $norm was 1, so this is our no-arg "default" behavior

        # Do nothing if a value is already set
        return if $$slot;

        # Set the default value of 'summary.json'
        return $$slot = 'summary.json';
    },
);
};
  • D multi-def multiple-default list-default array-default β€” this is "d" and "m" combined into one glorious thing. you can use the option multiple times to list multiple values, and you can call it with no args to add a set of "default" values. (not to be confused with THE default attribute, which is used even if the option never shows up on the command line, bestie, they are NOT the same thing πŸ’€)

real world example (simplified for doc purposes):

option dev_libs => (
    type  => 'D',
    short => 'D',
    name  => 'dev-lib',

    category    => 'Developer',
    description => 'Add paths to @INC before loading ANYTHING. This is what you use if you are developing yath or yath plugins to make sure the yath script finds the local code instead of the installed versions of the same code. You can provide an argument (-Dfoo) to provide a custom path, or you can just use -D without and arg to add lib, blib/lib and blib/arch.',

    long_examples  => ['', '=lib'],
    short_examples => ['', '=lib', 'lib'],

    # New way to specify the auto-fill values. This may be a single scalar,
    # or an arrayref.
    autofill => [ 'lib', 'blib/lib', 'blib/arch' ],

    # Old way to specify the auto-fill values.
    action => sub {
        my ($prefix, $field, $raw, $norm, $slot, $settings) = @_;

        # If no argument was provided use the 'lib', 'blib/lib', and 'blib/arch' defaults.
        # If an argument was provided, use it.
        push @{$$slot} => ($norm eq '1') ? ('lib', 'blib/lib', 'blib/arch') : ($norm);
    },
);
  • h hash β€” the hash type. every time the option is used it adds a single key/value pair to the hash, and you split the key from the value with an = sign. the option can be used multiple times, and a value is required. πŸ”‘
yath --opt foo=bar --opt baz=bat
  • H hash-list β€” basically the 'h' type, except the value for the key/value pair is expected to be a comma separated list, and it gets slotted under the key as an arrayref.
yath --opt foo=a,b,c --opt bar=1,2,3

that yath command above would produce this structure ✨:

{
    foo => ['a', 'b', 'c'],
    bar => ['1', '2', '3'],
}
  • $class->new(title => 'my_title')
  • $title = $opt->title()

you MUST specify either a title, or BOTH a name and a field. if you only give a title, that title is used to generate both the name and the field. πŸͺ„

so if your title is 'foo-bar_baz', your field becomes 'foo_bar_baz' and your name becomes '$PREFIX-foo-bar-baz'.

basically the title is what generates a sane field and/or name if neither one is specified. for the field, all dashes get changed to underscores, and the field is used as the key in the settings: "$settings->prefix->field". for the name, all underscores get changed to dashes, and if the option comes from a plugin then 'prefix-' gets prepended too. the name is what makes the command line argument --name.

if you don't want or don't like the name and field auto-generated from a title, just specify a name or a title directly, bestie. you're in charge. πŸ’…

  • $class->new(name => 'my-name')
  • $name = $opt->name()

you MUST specify either a title, or BOTH a name and field. if you only give a title, it generates the name and the field for you.

this name is your primary command line argument. if your name is "foo", your command line argument is --foo.

  • $class->new(field => 'my_field')
  • $field = $opt->field()

you MUST specify either a title, or BOTH a name and field. if you only give a title, it generates the name and the field for you.

the field is what's used in the settings hash. if your field is "foo", your settings path is "$setting->prefix->foo".

🌈 OPTIONAL

  • $class->new(action => sub ...)
  • $coderef = $opt->action()
option foo => (
    ...,
    action => sub {
        my ($prefix, $field_name, $raw_value, $normalized_value, $slot_ref, $settings, $handler, $options) = @_;

        # If no action is specified the following is all that is normally
        # done. Having an action means this is not done, so if you want the
        # value stored you must call this or similar.
        $handler->($slot, $normalized_value);
    },
);

so here's what all those args are πŸ‘‡

  • $prefix β€” the prefix for the option, the one you specified when you defined the option.
  • $field_name β€” the field for the option, the one you specified when you defined the option.
  • $raw_value β€” the value/argument that showed up on the command line. "--foo bar" would give us "bar". this is BEFORE any processing or normalizing happens, fr fr.

for options that don't take arguments, or where arguments are optional and none were provided, this will be '1'.

  • $normalized_value β€” if a normalize callback was provided, this is what you get after running $raw_value through that normalize callback.
  • $slot_ref β€” this is a scalar reference to the settings slot that holds the option value(s). 🫢

the default behavior when no action is specified is usually one of these:

$$slot_ref = $normalized_value;
push @{$$slot_ref} => $normalized_value;

However, to save yourself trouble you can use the $handler instead (see below).

  • $settings β€” the Test2::Harness::Settings instance. That's the actual settings object, bestie. πŸ’–
  • $handler β€” a callback that "does the right thing" as far as setting the value in the settings hash. This is literally what gets used when you don't set an action callback.
$handler->($slot, $normalized_value);
  • $options β€” the App::Yath::Options instance this option belongs to. Mainly useful if you've got an option that might add even more options (like how the --plugin option does it). Heads up though: if you go down that road you should also set the adds_options attribute to true. Skip that and the options list won't get refreshed, so your brand new options might not show up at all. 😭

✨ $class->new(adds_options => $bool) and $bool = $opt->adds_options()

If this is true, it means using this option could unlock more options. Example: loading a plugin. 🌈

✨ $class->new(alt => ['alt1', 'alt2', ...]) and $arrayref = $opt->alt()

Give your option alternative names. These are aliases that do the exact same thing on the command line β€” mainly a backcompat move for when an option gets renamed. πŸ’…

✨ $class->new(builds => 'My::Class') and $my_class = $opt->builds()

Use this when your option is part of constructing another object (like when the group it belongs to is made of options that map 1-to-1 onto fields in another object being built). The ultimate effect: an exception gets thrown if that class doesn't have the right attribute. Think of it as a safety net πŸ•ΈοΈ so you catch errors early when field names change, or when they're missing between this representation and the object being composed.

✨ $class->new(category => 'My Category') and $category = $opt->category()

This is what sorts/displays the help and POD documentation for your option. Leave it out and it gets set to 'NO CATEGORY - FIX ME'. The default is basically there so everyone knows you don't know what you're doing :-). πŸ’€

✨ $class->new(clear_env_vars => $bool) and $bool = $opt->clear_env_vars()

This one only does anything when it's paired up with the env_vars attribute.

Example:

option foo => (
    ...
    env_vars => ['foo', 'bar', 'baz'],
    clear_env_vars => 1,
):

So in this case you're saying option foo can be set from the value of $ENV{foo}, $ENV{bar}, or $ENV{baz} vars if any of them are defined. Then clear_env_vars tells it to delete those environment variables after they've been used to set the option. Super useful if you want the env var to set an option but you do not want any tests to be able to see that env var once it's been used. 🧼

✨ $class->new(default => $scalar) / $class->new(default => sub { return $default }) and $scalar_or_coderef = $opt->default()

Sets a default value for the field in the settings hash. Important: the default is set before any command line processing happens, so if the option never shows up on the command line, the default value is just... there. πŸ‘‘

Make sure you use the correct default value for your type. A scalar for 's', an arrayref for 'm', etc.

Note: for any non-scalar type you wanna use a subref to define the value:
option foo => (
    ...
    type => 'm',
    default => sub { [qw/a b c/] },
);

✨ $class->new(description => "Fe Fi Fo Fum") and $multiline_string = $opt->description()

Description of your option. It shows up in help output and POD. If you don't provide a value the default is 'NO DESCRIPTION - FIX ME'. πŸ™ˆ

✨ $class->new(env_vars => \@LIST) and $arrayref = $opt->env_vars()

If set, this should be an arrayref of environment variable names. If any of those environment variables are defined, then the settings get updated exactly as though the option had been provided on the command line with that value. ✨

Example:

option foo => (
    prefix => 'blah',
    type => 's',
    env_vars => ['FOO', 'BAR'],
);

Then this command line:

FOO="xxx" yath test

should be the same as

yath test --foo "xxx"

You can also ask to have the environment variables cleared once they've been checked:

option foo => (
    prefix => 'blah',
    type => 's',
    env_vars => ['FOO', 'BAR'],
    clear_env_vars => 1, # This tells yath to clear the env vars after they
    are used.
);

And if you'd like the option set to the opposite of the environment variable, you can prefix it with a '!' character:

option foo =>(
    ...
    env_vars => ['!FOO'],
);

In that case these two are equivalent:

FOO=0 yath test
yath test --foo=1
Careful though β€” this only works when the variable is defined. If $ENV{FOO} isn't defined, then the variable is not used. Periodt. 🫢

✨ $class->new(from_command => 'App::Yath::Command::COMMAND') and $cmd_class = $opt->from_command()

If your option was defined for a specific command, this gets set. You don't normally set this yourself β€” the tools in App::Yath::Options handle that for you. πŸ› οΈ

✨ $class->new(from_plugin => 'App::Yath::Plugin::PLUGIN') and $plugin_class = $opt->from_plugin()

If your option was defined for a specific plugin, this gets set. Again, you don't normally set this yourself β€” the tools in App::Yath::Options handle it for you. πŸ› οΈ

✨ $class->new(long_examples => [' foo', '=bar', ...]) and $arrayref = $opt->long_examples()

Just for documentation purposes. If your option takes arguments, you can drop examples here. The examples should not include the option itself β€” so "--foo bar" would be wrong, you should just do " bar". πŸ’…

✨ $class->new(negate => sub { ... }) and $coderef = $opt->negate()

Want a custom handler for negation (--no-OPT)? Provide one right here.

option foo => (
    ...
    negate => sub {
        my ($prefix, $field, $slot, $settings, $options) = @_;

        ...
    },
);

The variables are the same ones as in the action callback.

✨ $class->new(normalize => sub { ... }) and $coderef = $opt->normalize()

The normalize attribute holds a callback sub that takes the raw value as input and returns the normalized form. πŸͺ„

option foo => (
    ...,
    normalize => sub {
        my $raw = shift;

        ...

        return $norm;
    },
);

✨ $class->new(pre_command => $bool) and $bool = $opt->pre_command()

Options are either command-specific, or pre-command. Pre-command options are ones yath processes even if it hasn't figured out what command is being used yet. Great examples are --dev-lib and --plugin. πŸ‘‘

yath --pre-command-opt COMMAND --command-opt

Most of the time this should be false β€” very few options qualify as pre-command.

✨ $class->new(pre_process => sub { ... }) and $coderef = $opt->pre_process()

This is essentially a BEGIN block for options. This callback fires as soon as the option is parsed from the command line β€” well before the value gets normalized and added to settings. A great use for this: when your option needs to inject additional App::Yath::Option instances into the App::Yath::Options instance. πŸ’‰

option foo => (
    ...

    pre_process => sub {
        my %params = @_;

        my $opt     = $params{opt};
        my $options = $params{options};
        my $action  = $params{action};
        my $type    = $params{type};
        my $val     = $params{val};

        ...;
    },
);

Explanation of parameters:

  • $params{opt} β€” the opt instance.
  • $params{options} β€” the App::Yath::Options instance.
  • $params{action} β€” a string, usually either "handle" or "handle_negation".
  • $params{type} β€” a string, usually "pre-command" or "command ($CLASS)", where the second one has the command package in the parentheses.
  • $params{val} β€” the value being set, if any. For options that don't take arguments, or in the case of negation, this key may not exist at all. πŸ«₯

✨ $class->new(short => $single_character_string) and $single_character_string = $opt->short()

Want your option to be usable as a short option (single character, single dash -X)? Provide the character you wanna use right here. If the option doesn't require an argument, it can be combined with other no-argument short options β€” -xyz would be equivalent to -x -y -z. 🌟

There are only so many single characters to go around, so options are restricted to picking only 1.

Please note: Yath reserves the right to add any single-character short options in the main distribution. If they conflict with third party plugins/commands, then the third party must adapt and change its options. Because of that, it is not recommended to use any short options in third party addons. 🚨

✨ $class->new(short_examples => [' foo', ...]) and $arrayref = $opt->short_examples()

Just for documentation purposes. If your option takes arguments, you can drop examples here. The examples should not include the option itself β€” so "-f bar" would be wrong, you should just do " bar". πŸ’…

This attribute is not used if you don't provide a short attribute.

✨ $class->new(trace => [$package, $file, $line]) and $arrayref = $opt->trace()

This is almost always auto-populated for you via caller(). It should be an arrayref with a package, filename and line number. This is what gets used if there's a conflict between parameter names and/or short options. If that kind of situation ever comes up, the file/line number of all the conflicting options gets reported so it can be fixed. πŸ”

πŸ’… METHODS πŸ’…

okay bestie, this is the good part β€” all the stuff the object can do. every one of these is a method you call on your $opt object, and we're going through them one by one, no skips, no shortcuts 🫢

  • $bool = $opt->allows_arg() β€” returns true if args can be given to the option (it depends on the type, periodt). important nuance incoming: this being true does NOT mean the option must take an argument. 'D' type options can accept arguments, but they can also be used with no arguments at all. it's giving "flexible icon" πŸ’…
  • $bool = $opt->applicable($options) β€” if the option has an applicability callback set up, this runs it to figure out whether the option is even applicable given that App::Yath::Options instance. if no callback was provided, it just returns true, no drama πŸ’–
  • $character = $opt->canon_type($type_name) β€” hand it a long alias for an option type and it gives you back the single-character canonical name. heads up: it returns undef for any unknown strings. and the way it's not translating single-character names to themselves?? so $opt->canon_type('s') returns undef, while $opt->canon_type('string') returns 's'. yeah. that's the vibe. iykyk πŸ™ˆ
  • $val = $opt->get_default() β€” gives you the proper default value for the option. if a custom default was set, that's what you get; otherwise it falls back to the correct generic default for that option's type.

here's a snippet showing the defaults for the types:

# First check env vars and return any values from there
...
# Then check for a custom default and use it.
...

return 0
    if $self->{+TYPE} eq 'c'
    || $self->{+TYPE} eq 'b';

return []
    if $self->{+TYPE} eq 'm'
    || $self->{+TYPE} eq 'D';

return {}
    if $self->{+TYPE} eq 'h'
    || $self->{+TYPE} eq 'H';

# All others get undef
return undef;
  • $val $opt->get_normalized($raw) β€” converts a raw value into a normalized one. if a custom "normalize" attribute was set, that gets used; otherwise it normalizes according to the type. this is literally where booleans become 0 or 1, hashes get split, hash-lists get split even further, etc. ✨
  • $opt->handle($raw, $settings, $options, $list) β€” this method handles setting the value in $settings. lowkey a warning: you should not normally need to call this yourself. leave it to the machine bestie πŸ™ˆ
  • $opt->handle_negation() β€” this method handles a negated option. same deal as above: you should not normally need to call this yourself πŸ’€
  • @list = $opt->long_args() β€” returns the name plus any aliases. boom, list acquired πŸ‘‘
  • $ref = $opt->option_slot($settings) β€” gets you the settings->prefix->field reference. it creates the setting field if it's not already there, so it's not me being extra, it's just doing its job πŸŽ€
  • $bool = $opt->requires_arg() β€” returns true if this option requires an argument when it's used. no argument, no peace.
  • $string = $opt->trace_string() β€” returns a string like "somefile.pm line 42" based on where the option was defined. cute little receipt of where it came from 🧾
  • $bool = $opt->valid_type($character) β€” checks whether a single character type is valid. simple, clean, iconic.

πŸŽ€ DOCUMENTATION GENERATION πŸŽ€

yeah the options can generate their own docs, main character energy fr πŸ’…

  • $string = $opt->cli_docs() β€” gives you the option documentation in a format that works for the "yath help COMMAND" command.
  • $string = $opt->pod_docs() β€” gives you the option documentation in POD format, like so:
=item ....

.. option details ...

🌈 SOURCE 🌈

the source code repository for Test2-Harness lives at http://github.com/Test-More/Test2-Harness/. go peek, bestie, it's right there 🫢

πŸ‘‘ MAINTAINERS πŸ‘‘

Chad Granum <exodist@cpan.org>

⭐ AUTHORS ⭐

Chad Granum <exodist@cpan.org>

Copyright 2020 Chad Granum <exodist7@gmail.com>.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

See http://dev.perl.org/licenses/


and that's a wrap on the whole page, bestie!! πŸŽ‰ this was perl v5.40.1, dated 2026-05-10, and honestly? iconic of her. go forth and option responsibly πŸ’…βœ¨πŸŒΈ

πŸ“¦ where this page comes from