From 10d41cc50808d83a7c25d820f05cf6025857e216 Mon Sep 17 00:00:00 2001 From: Rafael Gibim <9031589+Drowze@users.noreply.github.com> Date: Thu, 9 Jul 2026 16:23:23 -0300 Subject: [PATCH] Support repeatable :array options Support repeatable :array options (in addition to comma-separated) ``` option :deps, type: :array # Before --deps=foo --deps=bar options[:deps] # => ["bar"] # After --deps=foo --deps=bar options[:deps] # => ["foo", "bar"] ``` --- lib/dry/cli/option.rb | 4 ++-- lib/dry/cli/parser.rb | 29 ++++++++++++++++++------ spec/support/shared_examples/commands.rb | 10 ++++++++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/lib/dry/cli/option.rb b/lib/dry/cli/option.rb index e50c850..a37be03 100644 --- a/lib/dry/cli/option.rb +++ b/lib/dry/cli/option.rb @@ -145,8 +145,8 @@ def valid_value?(value) def cast(value) return value unless cast_callable.respond_to?(:call) - if type == :array - value.map { |el| cast_single(el) } + if array? + Array(value).map { |el| cast_single(el) } else cast_single(value) end diff --git a/lib/dry/cli/parser.rb b/lib/dry/cli/parser.rb index eeed73a..6258393 100644 --- a/lib/dry/cli/parser.rb +++ b/lib/dry/cli/parser.rb @@ -17,13 +17,7 @@ def self.call(command, arguments, prog_name) original_arguments = arguments.dup parsed_options = {} - OptionParser.new do |opts| - command.options.each do |option| - opts.on(*option.parser_options) do |value| - parsed_options[option.name.to_sym] = option.cast(value) - end - end - + option_parser(command.options, parsed_options) do |opts| opts.on_tail("-h", "--help") do return Result.help end @@ -37,6 +31,27 @@ def self.call(command, arguments, prog_name) Result.failure(exception.message) end + # @since NEXT + # @api private + def self.option_parser(command_options, parsed_options) + OptionParser.new do |opts| + command_options.each do |option| + option_name = option.name.to_sym + opts.on(*option.parser_options) do |value| + value = option.cast(value) + if option.array? + parsed_options[option_name] ||= [] + parsed_options[option_name] += value + else + parsed_options[option_name] = value + end + end + end + + yield(opts) if block_given? + end + end + # @since 0.1.0 # @api private # diff --git a/spec/support/shared_examples/commands.rb b/spec/support/shared_examples/commands.rb index 31c3ec0..946cbd3 100644 --- a/spec/support/shared_examples/commands.rb +++ b/spec/support/shared_examples/commands.rb @@ -160,6 +160,16 @@ output = capture_output { cli.call(arguments: %w[exec test api admin]) } expect(output).to eq("exec - Task: test - Directories: [\"api\", \"admin\"]\n") end + + it "captures repeated array options" do + output = capture_output { cli.call(arguments: %w[server --deps=dep42 --deps=dep43]) } + + if RUBY_VERSION < "3.4" + expect(output).to eq("server - {:code_reloading=>true, :deps=>[\"dep42\", \"dep43\"]}\n") + else + expect(output).to eq("server - {code_reloading: true, deps: [\"dep42\", \"dep43\"]}\n") + end + end end context "with supported values" do