Commander
The complete solution for Ruby command-line executables. Commander bridges the gap between other terminal related libraries you know and love (OptionParser, HighLine), while providing many new features, and an elegant API.
Features
- Easier than baking cookies
- Parses options using OptionParser
- Auto-populates struct with options ( no more
{ |v| options[:recursive] = v }
) - Auto-generates help documentation via pluggable help formatters
- Optional default command when none is present
- Global / Command level options
- Packaged with two help formatters (Terminal, TerminalCompact)
- Imports the highline gem for interacting with the terminal
- Adds additional user interaction functionality
- Highly customizable progress bar with intuitive, simple usage
- Multi-word command name support such as
drupal module install MOD
, rather thandrupal module_install MOD
- Sexy paging for long bodies of text
- Support for MacOS text-to-speech
- Command aliasing (very powerful, as both switches and arguments can be used)
- Growl notification support for MacOS
- Use the
commander
executable to initialize a commander driven program
Installation
$ gem install commander
Quick Start
To generate a quick template for a commander app, run:
$ commander init yourfile.rb
To generate a quick modular style template for a commander app, run:
$ commander init --modular yourfile.rb
Example
For more option examples view the Commander::Command#option
method. Also
an important feature to note is that action may be a class to instantiate,
as well as an object, specifying a method to call, so view the RDoc for more information.
Classic style
require 'rubygems'
require 'commander/import'
# :name is optional, otherwise uses the basename of this executable
program :name, 'Foo Bar'
program :version, '1.0.0'
program :description, 'Stupid command that prints foo or bar.'
command :foo do |c|
c.syntax = 'foobar foo'
c.description = 'Displays foo'
c.action do |args, |
say 'foo'
end
end
command :bar do |c|
c.syntax = 'foobar bar [options]'
c.description = 'Display bar with optional prefix and suffix'
c.option '--prefix STRING', String, 'Adds a prefix to bar'
c.option '--suffix STRING', String, 'Adds a suffix to bar'
c.action do |args, |
.default :prefix => '(', :suffix => ')'
say "#{.prefix}bar#{.suffix}"
end
end
Example output:
$ foobar bar
# => (bar)
$ foobar bar --suffix '}' --prefix '{'
# => {bar}
Modular style
NOTE: Make sure to use require 'commander'
rather than require 'commander/import'
, otherwise Commander methods will still be imported into the global namespace.
require 'rubygems'
require 'commander'
class MyApplication
include Commander::Methods
def run
program :name, 'Foo Bar'
program :version, '1.0.0'
program :description, 'Stupid command that prints foo or bar.'
command :foo do |c|
c.syntax = 'foobar foo'
c.description = 'Displays foo'
c.action do |args, |
say 'foo'
end
end
run!
end
end
MyApplication.new.run if $0 == __FILE__
Block style
require 'rubygems'
require 'commander'
Commander.configure do
program :name, 'Foo Bar'
program :version, '1.0.0'
program :description, 'Stupid command that prints foo or bar.'
# see classic style example for options
end
HighLine
As mentioned above, the highline gem is imported into the global scope. Here are some quick examples for how to utilize highline in your commands:
# Ask for password masked with '*' character
ask("Password: ") { |q| q.echo = "*" }
# Ask for password
ask("Password: ") { |q| q.echo = false }
# Ask if the user agrees (yes or no)
agree("Do something?")
# Asks on a single line (note the space after ':')
ask("Name: ")
# Asks with new line after "Description:"
ask("Description:")
# Calls Date#parse to parse the date string passed
ask("Birthday? ", Date)
# Ensures Integer is within the range specified
ask("Age? ", Integer) { |q| q.in = 0..105 }
# Asks for a list of strings, converts to array
ask("Fav colors?", Array)
HighLine & Interaction Additions
In addition to highline's fantastic choice of methods, commander adds the following methods to simplify common tasks:
# Ask for password
password
# Ask for password with specific message and mask character
password "Enter your password please:", '-'
# Ask for CLASS, which may be any valid class responding to #parse. Date, Time, Array, etc
names = ask_for_array 'Names: '
bday = ask_for_date 'Birthday?: '
# Simple progress bar (Commander::UI::ProgressBar)
uris = %w[
http://vision-media.ca
http://google.com
http://yahoo.com
]
progress uris do |uri|
res = open uri
# Do something with response
end
# 'Log' action to stdout
log "create", "path/to/file.rb"
# Enable paging of output after this point
enable_paging
# Ask editor for input (EDITOR environment variable or whichever is available: TextMate, vim, vi, emacs, nano, pico)
ask_editor
# Ask editor, supplying initial text
ask_editor 'previous data to update'
# Ask editor, preferring a specific editor
ask_editor 'previous data', 'vim'
# Choose from an array of elements
choice = choose("Favorite language?", :ruby, :perl, :js)
# Alter IO for the duration of the block
io new_input, new_output do
new_input_contents = $stdin.read
puts new_input_contents # outputs to new_output stream
end
# $stdin / $stdout reset back to original streams
# Speech synthesis
speak 'What is your favorite food? '
food = ask 'favorite food?: '
speak "Wow, I like #{food} too. We have so much in common."
speak "I like #{food} as well!", "Victoria", 190
# Execute arbitrary applescript
applescript 'foo'
# Converse with speech recognition server
case converse 'What is the best food?', :cookies => 'Cookies', :unknown => 'Nothing'
when :cookies
speak 'o.m.g. you are awesome!'
else
case converse 'That is lame, shall I convince you cookies are the best?', :yes => 'Ok', :no => 'No', :maybe => 'Maybe another time'
when :yes
speak 'Well you see, cookies are just fantastic, they melt in your mouth.'
else
speak 'Ok then, bye.'
end
end
Growl Notifications
Commander provides methods for displaying Growl notifications. To use these methods you need to install https://github.com/tj/growl which utilizes the growlnotify executable. Note that growl is auto-imported by Commander when available, no need to require.
# Display a generic Growl notification
notify 'Something happened'
# Display an 'info' status notification
notify_info 'You have #{emails.length} new email(s)'
# Display an 'ok' status notification
notify_ok 'Gems updated'
# Display a 'warning' status notification
notify_warning '1 gem failed installation'
# Display an 'error' status notification
notify_error "Gem #{name} failed"
Commander Goodies
Option Defaults
The options struct passed to #action
provides a #default
method, allowing you
to set defaults in a clean manner for options which have not been set.
command :foo do |c|
c.option '--interval SECONDS', Integer, 'Interval in seconds'
c.option '--timeout SECONDS', Integer, 'Timeout in seconds'
c.action do |args, |
.default \
:interval => 2,
:timeout => 60
end
end
Command Aliasing
Aliases can be created using the #alias_command
method like below:
command :'install gem' do |c|
c.action { puts 'foo' }
end
alias_command :'gem install', :'install gem'
Or more complicated aliases can be made, passing any arguments as if it was invoked via the command line:
command :'install gem' do |c|
c.syntax = 'install gem <name> [options]'
c.option '--dest DIR', String, 'Destination directory'
c.action { |args, | puts "installing #{args.first} to #{.dest}" }
end
alias_command :update, :'install gem', 'rubygems', '--dest', 'some_path'
$ foo update
# => installing rubygems to some_path
Command Defaults
Although working with a command executable framework provides many
benefits over a single command implementation, sometimes you still
want the ability to create a terse syntax for your command. With that
in mind we may use #default_command
to help with this. Considering
our previous :'install gem'
example:
default_command :update
$ foo
# => installing rubygems to some_path
Keeping in mind that commander searches for the longest possible match
when considering a command, so if you were to pass arguments to foo
like below, expecting them to be passed to :update
, this would be incorrect,
and would end up calling :'install gem'
, so be careful that the users do
not need to use command names within the arguments.
$ foo install gem
# => installing to
Long descriptions
If you need to have a long command description, keep your short description under summary
, and consider multi-line strings for description
:
program :summary, 'Stupid command that prints foo or bar.'
program :description, %q(
#{c.summary}
More information about that stupid command that prints foo or bar.
And more
)
Additional Global Help
Arbitrary help can be added using the following #program
symbol:
program :help, 'Author', 'TJ Holowaychuk <[email protected]>'
Which will output the rest of the help doc, along with:
AUTHOR:
TJ Holowaychuk <[email protected]>
Global Options
Although most switches will be at the command level, several are available by
default at the global level, such as --version
, and --help
. Using
#global_option
you can add additional global options:
global_option('-c', '--config FILE', 'Load config data for your commands to use') { |file| ... }
This method accepts the same syntax as Commander::Command#option
so check it out for documentation.
All global options regardless of providing a block are accessable at the command level. This means that instead of the following:
global_option('--verbose') { $verbose = true }
...
c.action do |args, options|
say 'foo' if $verbose
...
You may:
global_option '--verbose'
...
c.action do |args, options|
say 'foo' if options.verbose
...
Formatters
Two core formatters are currently available, the default Terminal
formatter
as well as TerminalCompact
. To utilize a different formatter simply use
:help_formatter
like below:
program :help_formatter, Commander::HelpFormatter::TerminalCompact
Or utilize the help formatter aliases:
program :help_formatter, :compact
This abstraction could be utilized to generate HTML documentation for your executable.
Tracing
By default the -t
and --trace
global options are provided to allow users to get a backtrace to aid debugging.
You can disable these options:
never_trace!
Or make it always on:
always_trace!
Tips
When adding a global or command option, OptionParser implicitly adds a small
switch even when not explicitly created, for example -c
will be the same as
--config
in both examples, however -c
will only appear in the documentation
when explicitly assigning it.
global_option '-c', '--config FILE'
global_option '--config FILE'
ASCII Tables
For feature rich ASCII tables for your terminal app check out the terminal-table gem at https://github.com/tj/terminal-table
+----------+-------+----+--------+-----------------------+
| Terminal | Table | Is | Wicked | Awesome |
+----------+-------+----+--------+-----------------------+
| | | | | get it while its hot! |
+----------+-------+----+--------+-----------------------+
Running Specifications
$ rake spec
OR
$ spec --color spec
Contrib
Feel free to fork and request a pull, or submit a ticket https://github.com/commander-rb/commander/issues
License
This project is available under the MIT license. See LICENSE for details.