Skip to content

Be strict about required arguments; fix ordering bug - #177

Merged
timriley merged 3 commits into
mainfrom
required-arguments-with-defaults
Oct 10, 2026
Merged

timriley merged 3 commits into
mainfrom
required-arguments-with-defaults

Conversation

@timriley

@timriley timriley commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

While working on merging #122 (focusing on options with required: true), it became clear that we needed stricter handling of arguments with required: true — we were allowing required: true arguments to also have a default:, but didn't do anything with that default value.

Arguments are more fragile than options, because they're given positionally, versus being able to be supplied by name and in any order. So we handle conflicting options on arguments more strictly. Now we raise a Dry::CLI::CommandDefinitionError in a few cases:

  • If an argument with required: true is defined after one that is not required
  • If an argument is defined with both required: true and a default:.

We also fix a bug from when an argument is redefined. If someone e.g. changes an existing argument's description, it was previously moved to the end of this list, and would result in values arriving in an unexpected order. Now we allow the argument to be modified while keeping its place in the sequence.

Finally, now that we've introduced the broader CommandDefinitionError, we swap IncompatibleOptionError to use this one, just to keep things simple :)

@timriley
timriley requested a review from a team October 8, 2026 20:59
Comment thread lib/dry/cli/command.rb Outdated
Comment thread CHANGELOG.md Outdated
@timriley

timriley commented Oct 8, 2026

Copy link
Copy Markdown
Member Author

Thanks for this quick feedback, @parndt! I think I might actually change tack a bit here. I'm going to address your bug about redefining args (this is important!) but instead of automagically turning an arg with required: and a default: into an optional arg, I'm going to raise an exception instead. For things as fragile as positional arguments, I think it's better we ask the user to get things right.

@timriley
timriley marked this pull request as draft October 8, 2026 22:11
@timriley
timriley force-pushed the required-arguments-with-defaults branch from 9302bfd to 57a1127 Compare October 9, 2026 01:11
@timriley timriley changed the title Treat arguments with defaults as optional Be strict about required arguments; fix ordering bug Oct 9, 2026
@timriley
timriley force-pushed the required-arguments-with-defaults branch from 57a1127 to efa89a3 Compare October 9, 2026 01:23
Comment thread lib/dry/cli/errors.rb
Comment thread CHANGELOG.md Outdated
- Improved error messages to be more precise and helpful (@katafrakt in #158)
- Accepted values are now listed the same way in help output as in error messages, separated by commas rather than slashes. (@timriley in #163)
- Declaring a required argument after an optional one now raises `Dry::CLI::CommandDefinitionError`. Arguments are matched by position, so such a command could never be called correctly: the required argument could not be given without also giving the optional one before it. (@timriley in #177)
- Declaring an argument with both `required: true` and a `default:` now raises `Dry::CLI::CommandDefinitionError`. Arguments are matched by position, so the default would make the argument optional when it comes last, and would never be used anywhere else. Remove either `required: true` or the default. (@timriley in #177)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declaring an argument with required: true and a default used to load fine, and the default was just ignored. Now the command fails as soon as it's defined, so people with existing commands like that will see it after upgrading. Should we call this out as a breaking change?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, I'll add this before merging :)

@timriley
timriley force-pushed the required-arguments-with-defaults branch from efa89a3 to a76e43c Compare October 9, 2026 01:42
@timriley
timriley marked this pull request as ready for review October 9, 2026 01:44
@parndt
parndt added this pull request to stack #179 October 9, 2026 05:55
@timriley
timriley force-pushed the required-arguments-with-defaults branch 2 times, most recently from 4e05e06 to 53da0be Compare October 10, 2026 11:03
Since arguments are given by position, a required argument makes a previous optional argument non-optional.
Argument handling is quite fragile due to them being given positionally, so handle these strictly (unlike options, where we allow the default to make a required option non-required).
Now that we have this broader error class, let’s just use it for all definition errors.
@timriley
timriley force-pushed the required-arguments-with-defaults branch from 53da0be to 432fc2e Compare October 10, 2026 11:15
@timriley
timriley merged commit 5690678 into main Oct 10, 2026
7 checks passed
@timriley
timriley deleted the required-arguments-with-defaults branch October 10, 2026 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants