Skip to content

Commit a197817

Browse files
authored
Add support for unlimited nested command prefixes (#149)
Previously, Dry::CLI supported grouped commands via prefixes, but only one level deep. This change extends the internal DSL to allow recursively nested `register` blocks, enabling deeply nested command hierarchies like: mycli foo bar baz qux This improves organization for complex CLI apps with multiple levels of subcommands.
1 parent 64d8b19 commit a197817

File tree

5 files changed

+72
-1
lines changed

5 files changed

+72
-1
lines changed

lib/dry/cli/registry.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,9 +318,18 @@ def initialize(registry, prefix, aliases, hidden)
318318
# @since 0.1.0
319319
#
320320
# @see Dry::CLI::Registry#register
321-
def register(name, command, aliases: [], hidden: false)
321+
def register(name, command = nil, aliases: [], hidden: false, &block)
322322
command_name = "#{prefix} #{name}"
323323
registry.set(command_name, command, aliases, hidden)
324+
325+
if block_given?
326+
prefix = self.class.new(registry, command_name, aliases, hidden)
327+
if block.arity.zero?
328+
prefix.instance_eval(&block)
329+
else
330+
yield(prefix)
331+
end
332+
end
324333
end
325334

326335
private

spec/integration/rendering_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
foo generate [SUBCOMMAND]
1818
foo greeting [RESPONSE]
1919
foo hello # Print a greeting
20+
foo nested [SUBCOMMAND]
2021
foo new PROJECT # Generate a new Foo project
2122
foo root-command [ARGUMENT|SUBCOMMAND] # Root command with arguments and subcommands
2223
foo routes # Print routes

spec/integration/spell_checker_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
foo generate [SUBCOMMAND]
1919
foo greeting [RESPONSE]
2020
foo hello # Print a greeting
21+
foo nested [SUBCOMMAND]
2122
foo new PROJECT # Generate a new Foo project
2223
foo root-command [ARGUMENT|SUBCOMMAND] # Root command with arguments and subcommands
2324
foo routes # Print routes

spec/integration/subcommands_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,30 @@
5757
expect(output).to eq(expected)
5858
end
5959
end
60+
61+
context "it works with multi nested prefixed commands" do
62+
it "nests once" do
63+
output = `foo nested one`
64+
65+
expected = "I'm a one level nested command\n"
66+
67+
expect(output).to eq(expected)
68+
end
69+
70+
it "nests twice" do
71+
output = `foo nested one two`
72+
73+
expected = "I'm a two level nested command\n"
74+
75+
expect(output).to eq(expected)
76+
end
77+
78+
it "nests thrice" do
79+
output = `foo nested one two three`
80+
81+
expected = "I'm a three level nested command\n"
82+
83+
expect(output).to eq(expected)
84+
end
85+
end
6086
end

spec/support/fixtures/foo

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,32 @@ module Foo
433433
end
434434
end
435435
end
436+
437+
module PrefixCommands
438+
class NestedOnceCommand < Dry::CLI::Command
439+
desc "A nested sub command"
440+
441+
def call(**_params)
442+
puts "I'm a one level nested command"
443+
end
444+
end
445+
446+
class NestedTwiceCommand < Dry::CLI::Command
447+
desc "A nested nested sub command"
448+
449+
def call(**_params)
450+
puts "I'm a two level nested command"
451+
end
452+
end
453+
454+
class NestedThriceCommand < Dry::CLI::Command
455+
desc "A nested nested nested sub command"
456+
457+
def call(**_params)
458+
puts "I'm a three level nested command"
459+
end
460+
end
461+
end
436462
end
437463
end
438464
end
@@ -481,6 +507,14 @@ Foo::CLI::Commands.register "variadic default", Foo::CLI::Com
481507
Foo::CLI::Commands.register "variadic with-mandatory", Foo::CLI::Commands::MandatoryAndVariadicArguments
482508
Foo::CLI::Commands.register "variadic with-mandatory-and-options", Foo::CLI::Commands::MandatoryOptionsAndVariadicArguments
483509

510+
Foo::CLI::Commands.register "nested" do
511+
register "one", Foo::CLI::Commands::PrefixCommands::NestedOnceCommand do
512+
register "two", Foo::CLI::Commands::PrefixCommands::NestedTwiceCommand do
513+
register "three", Foo::CLI::Commands::PrefixCommands::NestedThriceCommand
514+
end
515+
end
516+
end
517+
484518
module Foo
485519
module Webpack
486520
module CLI

0 commit comments

Comments
 (0)