Introduction
dry-cli
is a general-purpose framework for developing Command Line Interface (CLI) applications. It represents commands as objects that can be registered and offers support for arguments, options and forwarding variadic arguments to a sub-command.
Usage
The following is an elaborate example showcasing most of the available features.
Imagine you want to build a CLI executable foo
for your Ruby project. The entire program is defined as below:
#!/usr/bin/env ruby
require "bundler/setup"
require "dry/cli"
module Foo
module CLI
module Commands
extend Dry::CLI::Registry
class Version < Dry::CLI::Command
desc "Print version"
def call(*)
puts "1.0.0"
end
end
class Echo < Dry::CLI::Command
desc "Print input"
argument :input, desc: "Input to print"
example [
" # Prints 'wuh?'",
"hello, folks # Prints 'hello, folks'"
]
def call(input: nil, **)
if input.nil?
puts "wuh?"
else
puts input
end
end
end
class Start < Dry::CLI::Command
desc "Start Foo machinery"
argument :root, required: true, desc: "Root directory"
example [
"path/to/root # Start Foo at root directory"
]
def call(root:, **)
puts "started - root: #{root}"
end
end
class Stop < Dry::CLI::Command
desc "Stop Foo machinery"
option :graceful, type: :boolean, default: true, desc: "Graceful stop"
def call(**options)
puts "stopped - graceful: #{options.fetch(:graceful)}"
end
end
class Exec < Dry::CLI::Command
desc "Execute a task"
argument :task, type: :string, required: true, desc: "Task to be executed"
argument :dirs, type: :array, required: false, desc: "Optional directories"
def call(task:, dirs: [], **)
puts "exec - task: #{task}, dirs: #{dirs.inspect}"
end
end
module Generate
class Configuration < Dry::CLI::Command
desc "Generate configuration"
option :apps, type: :array, default: [], aliases: ["-a"], desc: "Generate configuration for specific apps"
def call(apps:, **)
puts "generated configuration for apps: #{apps.inspect}"
end
end
class Test < Dry::CLI::Command
desc "Generate tests"
option :framework, default: "minitest", values: %w[minitest rspec]
def call(framework:, **)
puts "generated tests - framework: #{framework}"
end
end
end
register "version", Version, aliases: ["v", "-v", "--version"]
register "echo", Echo
register "start", Start
register "stop", Stop
register "exec", Exec
register "generate", aliases: ["g"] do |prefix|
prefix.register "config", Generate::Configuration
prefix.register "test", Generate::Test
end
end
end
end
Dry::CLI.new(Foo::CLI::Commands).call
Available commands
With this code in place, we can now have a look at the command line usage by issuing the command foo
without any arguments or options:
$ foo
Commands:
foo echo [INPUT] # Print input
foo exec TASK [DIRS] # Execute a task
foo generate [SUBCOMMAND]
foo start ROOT # Start Foo machinery
foo stop # Stop Foo machinery
foo version # Print version
Help
It is also possible to get help for a particular command using the --help
flag:
$ foo echo --help
Command:
foo echo
Usage:
foo echo [INPUT]
Description:
Print input
Arguments:
INPUT # Input to print
Options:
--help, -h # Print this help
Examples:
foo echo # Prints 'wuh?'
foo echo hello, folks # Prints 'hello, folks'
Optional arguments
A command can have optional arguments, which enables a default action in case nothing is provided:
$ foo echo
wuh?
$ foo echo hello
hello
Required arguments
On the other hand, required arguments will throw an error if not provided:
$ foo start .
started - root: .
$ foo start
ERROR: "foo start" was called with no arguments
Usage: "foo start ROOT"
Array arguments
Captures all the remaining arguments in a single array.
Please note that array
argument must be used as the last argument, as it works as a "catch-all".
$ foo exec test
exec - task: test, dirs: []
$ foo exec test spec/bookshelf/entities spec/bookshelf/repositories
exec - task: test, dirs: ["spec/bookshelf/entities", "spec/bookshelf/repositories"]
Options
An option is a named argument that is passed after the command name and the arguments:
$ foo generate test
generated tests - framework: minitest
$ foo generate test --framework=rspec
generated tests - framework: rspec
$ foo generate test --framework=unknown
ERROR: "foo generate test" was called with arguments "--framework=unknown"
Boolean options
Boolean options are flags that change the behaviour of a command:
$ foo stop
stopped - graceful: true
$ foo stop --no-graceful
stopped - graceful: false
Array options
Array options are similar to arguments but must be named:
$ foo generate config --apps=web,api
generated configuration for apps: ["web", "api"]
Subcommands
Subcommands are simply commands that have been registered under a nested path:
$ foo generate
Commands:
foo generate config # Generate configuration
foo generate test # Generate tests
Aliases
Aliases are supported:
$ foo version
1.0.0
$ foo v
1.0.0
$ foo -v
1.0.0
$ foo --version
1.0.0
Subcommand aliases
Work similarly to command aliases
$ foo g config
generated configuration for apps: []