General purpose Command Line Interface (CLI) framework for Ruby.
thor
gem replacement), NOT the implementation of the hanami
CLI commands
For a given command name, you can register a corresponding command object (aka command).
Example: for foo hi
command name there is the corresponding Foo::CLI::Hello
command object.
#!/usr/bin/env ruby
require "bundler/setup"
require "hanami/cli"
module Foo
module CLI
module Commands
extend Hanami::CLI::Registry
class Hello < Hanami::CLI::Command
def call(*)
end
end
end
end
end
class Version < Hanami::CLI::Command
def call(*)
end
end
Foo::CLI::Commands.register "hi", Foo::CLI::Commands::Hello
Foo::CLI::Commands.register "v", Version
Hanami::CLI.new(Foo::CLI::Commands).call
Please note: there is NOT a convention between the command name and the command object class. The manual registration assigns a command object to a command name.
A command is a subclass of Hanami::CLI::Command
and it MUST respond to #call(*)
.
There is nothing special in subcommands: they are just command objects registered under a nested command name.
#!/usr/bin/env ruby
require "bundler/setup"
require "hanami/cli"
module Foo
module CLI
module Commands
extend Hanami::CLI::Registry
module Generate
class Configuration < Hanami::CLI::Command
def call(*)
end
end
end
end
end
end
Foo::CLI::Commands.register "generate configuration", Foo::CLI::Commands::Generate::Configuration
Hanami::CLI.new(Foo::CLI::Commands).call
An argument is a token passed after the command name.
For instance, given the foo greet
command, when an user types foo greet Luca
, then Luca
is considered an argument.
A command can accept none or many arguments.
An argument can be declared as required.
#!/usr/bin/env ruby
require "bundler/setup"
require "hanami/cli"
module Foo
module CLI
module Commands
extend Hanami::CLI::Registry
class Greet < Hanami::CLI::Command
argument :name, required: true, desc: "The name of the person to greet"
argument :age, desc: "The age of the person to greet"
def call(name:, age: nil, **)
result = "Hello, #{name}."
result = "#{result} You are #{age} years old." unless age.nil?
puts result
end
end
register "greet", Greet
end
end
end
Hanami::CLI.new(Foo::CLI::Commands).call
% foo greet Luca
Hello, Luca.
% foo greet Luca 35
Hello, Luca. You are 35 years old.
% foo greet
ERROR: "foo greet" was called with no arguments
Usage: "foo greet NAME"
An option is a named argument that is passed after the command name and the arguments.
For instance, given the foo request
command, when an user types foo request --mode=http2
, then --mode=http2
is considered an option.
A command can accept none or many options.
#!/usr/bin/env ruby
require "bundler/setup"
require "hanami/cli"
module Foo
module CLI
module Commands
extend Hanami::CLI::Registry
class Request < Hanami::CLI::Command
option :mode, default: "http", values: %w[http http2], desc: "The request mode"
def call(**options)
puts "Performing a request (mode: #{options.fetch(:mode)})"
end
end
register "request", Request
end
end
end
Hanami::CLI.new(Foo::CLI::Commands).call
% foo request
Performing a request (mode: http)
% foo request --mode=http2
Performing a request (mode: http2)
% foo request --mode=unknown
Error: Invalid param provided
Add this line to your application's Gemfile:
gem "hanami-cli"
And then execute:
$ bundle
Or install it yourself as:
$ gem install hanami-cli
Imagine to build a CLI executable foo
for your Ruby project.
#!/usr/bin/env ruby
require "bundler/setup"
require "hanami/cli"
module Foo
module CLI
module Commands
extend Hanami::CLI::Registry
class Version < Hanami::CLI::Command
desc "Print version"
def call(*)
puts "1.0.0"
end
end
class Echo < Hanami::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 < Hanami::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 < Hanami::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
module Generate
class Configuration < Hanami::CLI::Command
desc "Generate configuration"
def call(*)
puts "generated configuration"
end
end
class Test < Hanami::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 "generate", aliases: ["g"] do |prefix|
prefix.register "config", Generate::Configuration
prefix.register "test", Generate::Test
end
end
end
end
Hanami::CLI.new(Foo::CLI::Commands).call
Let's have a look at the command line usage.
% foo
Commands:
foo echo [INPUT] # Print input
foo generate [SUBCOMMAND]
foo start ROOT # Start Foo machinery
foo stop # Stop Foo machinery
foo version # Print version
% 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'
% foo echo
wuh?
% foo echo hello
hello
% foo start .
started - root: .
% foo start
ERROR: "foo start" was called with no arguments
Usage: "foo start ROOT"
% foo generate test
generated tests - framework: minitest
% foo generate test --framework=rspec
generated tests - framework: rspec
% foo generate test --framework=unknown
Error: Invalid param provided
% foo stop
stopped - graceful: true
% foo stop --no-graceful
stopped - graceful: false
% foo generate
Commands:
foo generate config # Generate configuration
foo generate test # Generate tests
% foo version
1.0.0
% foo v
1.0.0
% foo -v
1.0.0
% foo --version
1.0.0
% foo g config
generated configuration
After checking out the repo, run bin/setup
to install dependencies. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/hanami/cli.
Copyright © 2017 Luca Guidi – Released under MIT License