= 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 sub-command when none is present * Global / Sub-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 intuative, simple usage * Multi-word command name support such as 'drupal module install MOD', rather than 'drupal module_install MOD' * Sexy paging for long bodies of text * Sub-command aliasing (very powerful, as both switches and arguments can be used) * Use the 'commander' executable to initialize a commander driven program == Example For more option examples view the Commander::Command#option method. Also an important feature to note is that when_called may be a class to instantiate, as well as an object, specifying a method to call, so view the RDoc for more information. require 'rubygems' require 'commander' # :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.when_called do |args, options| 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.when_called do |args, options| options.default :prefix => '(', :suffix => ')' say "#{options.prefix}bar#{options.suffix}" end end $ foobar bar # => (bar) $ foobar bar --suffix '{' --prefix '}' # => {bar} == HighLine As mentioned above the highline gem is imported into 'global scope', below are some quick examples for how to utilize highline in your command(s): # 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) # Provide a menu for users to choose from choose do |menu| menu.index = :letter menu.index_suffix = ") " menu.prompt = "Please choose your favorite programming language? " menu.choice :ruby do say("Good choice!") end menu.choices(:python, :perl) do say("Not from around here, are you?") end end # Custom shell loop do choose do |menu| menu.layout = :menu_only menu.shell = true menu.choice(:load, "Load a file.") do |command, details| say("Loading file with options: #{details}...") end menu.choice(:save, "Save a file.") do |command, details| say("Saving file with options: #{details}...") end menu.choice(:quit, "Exit program.") { exit } end end == HighLine & Interaction Additions In addition to highline's fantastic choice of methods we will continue to simplify common tasks using the following methods: # 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 or TextMate by default) ask_editor # Ask editor, supplying initial text ask_editor 'previous data to update' == Commander Goodies === Option Defaults The options struct passed to #when_called provides a #default method, allowing you to set defaults in a clean manor to 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.when_called do |args, options| options.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.when_called { 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 [options]' c.option '--dest DIR', String, 'Destination directory' c.when_called { |args, options| puts "installing #{args.first} to #{options.dest}" } end alias_command :update, :'install gem', 'rubygems', '--dest', 'some_path' $ foo update # => installing rubygems to some_path === Command Defaults Although working with a sub-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 sub-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 === Additional Global Help Arbitrary help can be added using the following #program symbol: program :help, 'Author', 'TJ Holowaychuk ' Which will output the rest of the help doc, along with: AUTHOR: TJ Holowaychuk === Global Options Although most switches will be at the sub-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 sub-command level. This means that instead of the following: global_option('--verbose') { $verbose = true } ... c.when_called do |args, options| say 'foo' if $verbose ... You may: global_option '--verbose' ... c.when_called 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 This abstraction could be utilized to generate HTML documentation for your executable. == Tips When adding a global or sub-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 visionmedia's terminal-table gem at http://github.com/visionmedia/terminal-table +----------+-------+----+--------+-----------------------+ | Terminal | Table | Is | Wicked | Awesome | +----------+-------+----+--------+-----------------------+ | | | | | get it while its hot! | +----------+-------+----+--------+-----------------------+ == Contrib Feel free to fork and request a pull, or submit a ticket http://visionmedia.lighthouseapp.com/projects/27643-commander == Known Issues * ask_editor has been tested with TextMate only == License (The MIT License) Copyright (c) 2008-2009 TJ Holowaychuk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.