Git Product home page Git Product logo

commander's Introduction

Build Status Inline docs

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 than drupal 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, 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.action do |args, options|
    options.default :prefix => '(', :suffix => ')'
    say "#{options.prefix}bar#{options.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, options|
        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, 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.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, 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 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.

commander's People

Contributors

andrewvc avatar badeball avatar belousovav avatar benbc avatar braintreeps avatar brandondrew avatar doriantaylor avatar fallwith avatar fgalassi avatar ggilder avatar jonathonma avatar josephpecoraro avatar kethomassen avatar krausefx avatar lacostej avatar lebogan avatar marcandre avatar markrebec avatar mattbrictson avatar mattes avatar mfurtak avatar olleolleolle avatar orien avatar rmc avatar rohitpaulk avatar rrrene avatar skytrix avatar suan avatar tj avatar toolmantim avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

commander's Issues

Cannot have switches that start with 'version'

Or other global long option names.

runner.rb:340 is the culprit as it essentially does a starts_with comparison.

As a test case, create an option named versionCode, which when specified on the command line, gets deleted after remove_global_options.

Provide heredoc helper

It would be great if commander would provide a helper to unintend a String created with a heredoc. Some seem to solve this "elegantly" via a core_ext to String:

class String
  def unindent
    gsub(/^#{scan(/^\s*/).min_by{|l|l.length}}/, "")
  end
end

command :test do
  message = <<-EOM.unindent
  This is a test message! The left indent
  gets stripped automatically!
  EOM
end

--[no-]blah style flags fail when used with `global_option`

There seems to be a problem when using '--[no-]blah' style flags as part of a global_option. Specifically, the flag appears to not be accounted for during bookkeeping in remove_global_options.

The following test program exhibits the problem:

#!/usr/bin/env ruby

require 'rubygems'
require 'commander'

class MyApplication
  include Commander::Methods

  def run
    runner = Commander::Runner.instance

    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|
        puts 'Command goes foo'
        puts 'Wow!' if $exclaim
      end
    end

    global_option('-e', '--[no-]exclaim', 'Whether not to exclaim') do |val| 
      $exclaim = val
      puts "$exclaim is: #{$exclaim}"
    end

    runner.parse_global_options

    puts "ARGV before global options removed: #{ARGV}"

    runner.remove_global_options(runner.options, ARGV)

    puts "ARGV after global options removed: #{ARGV}"

    run!
  end
end

MyApplication.new.run if $0 == __FILE__

When run with ./commander_example.rb foo --exclaim, the result is:

$exclaim is: true
ARGV before global options removed: ["foo", "--exclaim"]
ARGV after global options removed: ["foo", "--exclaim"]
$exclaim is: true
invalid option: --exclaim

When run with ./commander_example.rb foo --no-exclaim the result is:

$exclaim is: false
ARGV before global options removed: ["foo", "--no-exclaim"]
ARGV after global options removed: ["foo", "--no-exclaim"]
$exclaim is: false
invalid option: --no-exclaim

When run with ./commander_example.rb foo -e the result is:

$exclaim is: true
ARGV before global options removed: ["foo", "-e"]
ARGV after global options removed: ["foo"]
Command goes foo
Wow!

edit: Corrected the bug description, my test program, produced output, and provided one more example

Cannot pass '--trace' to alias_command

When trying to add a "debug" alias to a previously defined command, I can't pass the --trace flag to it, even though I could manually append --trace when running the command from CLI manually.

e.g.:

command :run do |c|
  c.syntax = "foobar run"
  c.example 'foobar run --thing path/to/thing --other-thing 12345'
  c.option '--thing STRING', String
  c.option '--other-thing STRING', String

  c.action do |_args, options|
    # do stuff here
  end
end
alias_command :debug, :run, '--thing', 'foo', '--other-thing', '67890', '--trace'

when running debug:

$ foobar debug
invalid option: --trace

amusingly, if I apply --trace to debug:

$ foobar debug

Traceback (most recent call last):
        5: from /Users/me/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/commander-4.4.4/lib/commander/import.rb:5:in `block in <top (required)>'
        4: from /Users/me/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/commander-4.4.4/lib/commander/delegates.rb:15:in `run!'
        3: from /Users/me/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/commander-4.4.4/lib/commander/runner.rb:68:in `run!'
        2: from /Users/me/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/commander-4.4.4/lib/commander/runner.rb:444:in `run_active_command'
        1: from /Users/me/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/commander-4.4.4/lib/commander/command.rb:153:in `run'
/Users/me/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/commander-4.4.4/lib/commander/command.rb:164:in `parse_options_and_call_procs': invalid option: --trace (OptionParser::InvalidOption)

It'd be great if I could hardcode always providing traceback for a specific alias, but not show the traceback by default. Is there an easy way to do this that I'm missing?

Recommended way to access args after --

I'm building an application that will ultimately call another script. I would like to have a way to delineate between the commands proper args and options versus whats after the --

doh command --option --name bob -- --pass 

How can I get everything after the -- using commander, or do I need to inspect the $* on my own.

Options with arguments may not be properly removed before executing command

Options with arguments may not be properly removed before executing command.

Test program:

require 'commander/import'

program :name, 'Name'
program :version, '0.1'
program :description, 'Issue with Global Option parsing.'

default_command :cmd

global_option('--target=STRING', String, 'Specify a target.') do |s|
  puts "Parsed Global Option: #{s}"
end

command :cmd do |c|
  c.action do
    puts 'Success'
  end
end

Example:

$ ruby test.rb --target 1
Parsed Global Option: 1
Success

$ ruby test.rb --target=1
Parsed Global Option: 1
invalid option: --target=1

Here it is clear that the Global Option handler for --target ran and received the expected argument 1. However this global option was not consumed before invoking the sub-command, and produced invalid option. Doing the same thing without an = sign works for both global option parsing and is removed before the sub-command.


Relevant code to look at is remove_global_option which I don't think correctly handles arguments that contains an =. In this case arg is --target=1 and the code tries to remove switches named --target with direct comparisons, which of course fails.

Specifically this part looks fishy:

  args.delete_if do |arg|
    if switches.any? { |s| s[0, arg.length] == arg }
      ...
    end
  end

Revisit documentation / clarification for subcommands #14

I have been playing around with the command aliasing feature and would like to take a crack at adding to the documentation. From what I can see this isn't a true sub-command structure but the potential for organizing commands with it is awesome.

This is what a sample help screen looks like:

bundle exec projman help
...
Commands:
  help ...

Aliases:                                                                      
    project list project list                                                 
    project new  project new                                                    
    project show project show                                                   
    task add     task add                                                       
    task delete  task delete                                                   
    task update  task update                                                   

I appreciate any advice you have on this matter.

Thank you.

Cannot define my own -v and --version params

The -v and --version default global options prevent me from defining my own -v and --version parameters to commands. (As the tool I'm writing deals with versions, this is a bummer.)

Is it really impossible to prevent Commander from adding these defaults, or have I missed something? In the former case, I'll be happy to submit a patch.

Commander is really great, thanks!

allow changing `-h` option

Hi, sometimes it's preferable to disable -h option and have only --help. This for example when hostname is to be specified. Please make -h option configurable. --help is fine, there can be no confusion.

Modular style gotcha

I have been using bundler, rake, and geminabox to build and distribute my gems locally. When I build a classic-style app via the above everything works as expected. Not so with the modular-style. Using 'bundle exec' works. Using 'rake install' does not. My app basically never runs. I traced using this snippet at the end of my app:

puts "Running #{$PROGRAM_NAME}"
puts "#{__FILE__}"
puts $PROGRAM_NAME == __FILE__
MyApplication.new.run #if $PROGRAM_NAME == __FILE__

Running 'bundle exec exe/modapp':

Running exe/modapp
exe/modapp
true

Running 'modapp [option]' after 'rake install' or 'gem install':

Running /home/lewisb/.rvm/gems/ruby-2.2.4/bin/modapp
/home/lewisb/Projects/commander_projects/modapp/exe/modapp
false

Is this expected behavior or am I missing something incredibly basic? I have been using mixins to organize my apps.

Thanks for any insights.

--help with additional --options gives 'command not found'

I often find mysql wanting to put --help on the end of a command to see the help text for that command. With commander, if I do that though, I first have to remove any --option options passed or I get 'command not found'

Example:

#!/usr/bin/env ruby

require 'rubygems'
require 'commander'

class MyApplication
  include Commander::Methods
  # include whatever modules you need

  def run
    program :name, 'test'
    program :version, '0.0.1'
    program :description, 'test'

    command :bob do |c|
      c.syntax = 'test bob [options]'
      c.summary = ''
      c.description = ''
      c.example 'description', 'command example'
      c.option '--some-switch', 'Some switch that does something'
      c.action do |args, options|
        # Do something or c.when_called Test::Commands::Bob
      end
    end

    run!
  end
end

MyApplication.new.run if $0 == __FILE__

Results in:

โžœ  ruby yourfile.rb --some-switch --help
invalid command. Use --help for more information

Is there any way to make this possible?

Nesting options or adding flags to options?

Does this even make sense?:

I'm building a simple Sinatra application builder with Commander. On my option to build with the inline method, I want to choose between Slim or Erb. It would go something like this:

$ sincab init --inline -s myapp
or
$ sincab init --inline -e myapp

Those two flags would only belong to the --inline option.

Thanks for any thoughts on this.

deprecation error when running with ruby 2.5.0

Seeing the following:

/usr/local/lib/ruby/gems/2.5.0/gems/commander-4.4.3/lib/commander/user_interaction.rb:334: warning: constant ::Data is deprecated

When running with ruby 2.5.0 on Mac OS High Sierra. Let me know if any other info is necessary and I'll provide it.

Display --help on missing option

I have a command with a required option. How can I display the help page to the user if this option is not set?:

    command :test do |c|
      c.syntax = 'Test command'
      c.description = 'Test command'
      c.option '--prefix STRING', String, 'Adds a prefix to test'
      c.action do |args, options|
        raise ArgumentError.new("Prefix is required!") unless options.prefix
        testMeth
      end
    end

Prevent default command from running when user specifies an unknown command

If I use default_command, any time someone runs an unknown command, Commander executes the default command. Is there any way to make Commander operate similar to bundler, and have it throw an error in this situation? I only want it run the default command when no sub command is specified.

$ bundle unknown_command
Could not find command "unknown_command".

$ bundle
Fetching gem metadata from https://rubygems.org/........

Global option values ignored when args provided

Global options are ignored when additional command line arguments are also provided.

Using the test harness below, I get the following results.

1. Without extra arguments provided โœ…

In this scenario without extra arguments provided, the command correctly uses the provided global option value.

./command.rb experiment --config config-value
args:   []
config: "config-value"

2. With extra arguments provided after the global config โŒ

However with arguments provided after the configuration value, the command incorrectly falls back to the default value.

./command.rb experiment --config config-value arg1 arg2
args:   ["arg1", "arg2"]
config: "default"

3. With extra arguments provided before the global config โŒ

Similarly with arguments provided before the configuration value, the command incorrectly falls back to the default value.

./command.rb experiment arg1 arg2 --config config-value
args:   ["arg1", "arg2"]
config: "default"

command.rb test harness

#!/usr/bin/env ruby

require 'rubygems'
require 'commander'

class MyApplication
  include Commander::Methods

  def run
    program :name, 'Example with global option'
    program :description, 'Example with global option'
    program :version, '1.0.0'
    global_option '-c', '--config CONFIG', String, 'A global option'

    command :experiment do |c|
      c.action do |args, options|
        options.default {config: 'default'}
        say "args:   #{args.inspect}"
        say "config: #{options.config.inspect}"
      end
    end

    run!
  end
end

MyApplication.new.run if $0 == __FILE__

The `find` statement fails on Windows

From @kodybrown on March 11, 2014 17:0

The following statement fails; user_interaction.rb line #240:

find {|name| system("hash #{name.split.first} 2>&-") }

I changed it to this, which now works:

find {|name| system("hash #{name.split.first} 2>&1") }

But, now I don't know where to find hash.. Is this a bash util or built-in lib somewhere? I tried gem install hash but it does not seem to be what it is looking for..

I'm running bro (on Windows) which uses commander..

Copied from original issue: tj/commander#69

state-changing behaviour inhibits `Commander::Command` reuse

Greets,

I may be completely abusing this library but I figured out I could reuse the command parsing functionality in a shell loop. Only one problem: when Commander::Command#call is called, it empties the array where the code is kept. The net effect is that you can only #run it once.

I don't know enough about this package to know if it would mess anything else up to make these objects effectively immutable, but even something like this:

def call(args = [])
  object, meth = @when_called[0,2]
  meth ||= :call
  options = proxy_option_struct
  case object
  when Proc then object.call(args, options)
  when Class then meth != :call ? object.new.send(meth, args, options) : object.new(args, options)
  else object.send(meth, args, options) if object
  end
end

โ€ฆwould be non-destructive. Then there is the matter of @proxy_options being continually appended to via #option_proc, so it would have to be emptied after each #run, perhaps like this:

def proxy_option_struct!
  out = proxy_option_struct
  @proxy_options = []
  out
end

โ€ฆand then just change that one line in #call. This is me just eyeballing it, so I have no idea if any of this would work/not break anything elsewhere. I can fork and PR this if there are no surprises.

Thanks,

documentation / clarification for subcommands

The features mention the use of multi-word commands / subcommands, but it's not immediately clear to those new to Ruby as to how that would be done.

Would it be possible to update the documentation to reflect subcommand examples?

Thank you in advance!

Usage without ARGV

Hi,

Is it possible to use this library with a custom Array instead of ARGV? I couldn't find any example of it and I can't figure out the code :/

Regression: `--help` no longer works anywhere on the command line

Given a sample program:

require 'commander/import'

program :name, 'Name'
program :version, '0.1'
program :description, 'Issue with Global Option parsing.'

command :cmd do |c|
  c.action do
    puts 'Success'
  end
end

Previously you could use the global --help anywhere on the line but now you can't. This used to provide the help for cmd, but no longer does:

$ ruby foo.rb cmd a --help
invalid command. Use --help for more information

Seems like a recent regression.

Global options not available at command level when placed before command name

At https://github.com/commander-rb/commander#global-options, it says:

---- begin snip ----

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
...

---- end snip ----

but I have been unable to get this to work.

I have:

global_option('-s', '--server SERVER', 'Host name or IP address') do |server|
  $server = server
end
...
command :repo_package_query do |c|
  c.syntax = 'aptly-cli repo_package_query [options]'
  c.summary = 'List all packages in local repository or perform search on repository contents and return result., requires --name'
  c.description = 'List all packages or search on repo contents, requires --name'
  c.example 'description', 'aptly-cli repo_package_query --name megatronsoftware -query geoipupdate'
  c.option '--name NAME', String, 'Local repository name, required'
  c.option '--query QUERY', String, 'Package to query'
  c.option '--with_deps', 'Return results with dependencies'
  c.option '--format FORMAT', String, 'Format type to return, compact by default. "details" is an option'
  c.action do |args, options|
    require 'pry'; binding.pry
...
end

And then I do this:

$ aptly-cli --server 10.3.0.46 repo_package_query --name develop --query 'Name (~ anonweb)'

From: /usr/local/lib/ruby/gems/2.3.0/gems/aptly_cli-0.2.9/bin/aptly-cli @ line 158 :

    153:   c.option '--query QUERY', String, 'Package to query'
    154:   c.option '--with_deps', 'Return results with dependencies'
    155:   c.option '--format FORMAT', String, 'Format type to return, compact by default. "details" is an option'
    156:   c.action do |args, options|
    157:     require 'pry'; binding.pry
 => 158:     config = AptlyCli::AptlyLoad.new.configure_with($config_file)
    159:     aptly_command = AptlyCli::AptlyRepo.new(config)
    160:     if options.query
    161:       repo_options = { :name => options.name.to_s, :query => options.query.to_s }
    162:     elsif options.with_deps and options.query.nil?
    163:       repo_options = { :name => options.name.to_s, :with_deps => options.with_deps.to_s }

[1] pry(main)> options
=> <Commander::Command::Options name="develop", query="Name (~ anonweb)">
[2] pry(main)> $server
=> "10.3.0.46"
[3] pry(main)> options.server
=> nil

Note that $server is set, but options.server is not set. Shouldn't it be? Or am I misunderstanding how it works?

Same behavior if I simply do:

global_option '--server SERVER'

and it also occurs with commands that don't take options of their own:

command :repo_list do |c|
  c.syntax = 'aptly-cli repo_list [options]'
  c.summary = 'Show list of currently available local repositories'
  c.description = 'Show list of currently available local repositories'
  c.example 'description', 'aptly-cli repo_list'
  c.action do |args, options|
    require 'pry'; binding.pry
...
end

Running it:

$ aptly-cli --server 10.3.0.46 repo_list

From: /usr/local/lib/ruby/gems/2.3.0/gems/aptly_cli-0.2.9/bin/aptly-cli @ line 141 :

    136:   c.summary = 'Show list of currently available local repositories'
    137:   c.description = 'Show list of currently available local repositories'
    138:   c.example 'description', 'aptly-cli repo_list'
    139:   c.action do |args, options|
    140:     require 'pry'; binding.pry
 => 141:     config = AptlyCli::AptlyLoad.new.configure_with($config_file)
    142:     aptly_command = AptlyCli::AptlyRepo.new(config, options)
    143:     puts aptly_command.repo_list()
    144:   end
    145: end
    146:

[1] pry(main)> options
=> <Commander::Command::Options >
[2] pry(main)> $server
=> "10.3.0.46"
[3] pry(main)> options.server
=> nil

System info

$ gem list commander

*** LOCAL GEMS ***

commander (4.4.0)

$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin14]

Include bash tab completion for commands

From @suan on March 1, 2012 17:30

This just struck me as something very useful and practical to include as a feature. I'm only very vaguely familiar on how bash tab completion works, so I have no idea of the work it would involve. If its a significant effort though I might be willing to help - its something that I've always wanted in my scripts!

Copied from original issue: tj/commander#41

Deprecation warning when used together with pg

When using both, your gem and the pg gem (for PostgreSQL) in a project, I get a deprecation warning from pg as they have deprecated some of their constants. To get the deprecation warning when using the old constants, pg moved them in a special file (
https://bitbucket.org/ged/ruby-pg/src/master/lib/pg/deprecated_constants.rb) that is not needed normaly. But as you are walking through all constants (https://github.com/commander-rb/commander/blob/master/lib/commander/user_interaction.rb#L334), also the deprecated constants are accessed.

Maybe you could add them to your deprecation list.

Option argument parsed as a global option

I'm using Commander 4.4.7. I am noticing that if I have an option that takes an argument, and the argument starts with a global option, the argument is parsed as a global option.

require 'commander/import'

program :name, 'Name'
program :version, '0.1'
program :description, 'Issue with argument parsing.'

default_command :mount

command :mount do |c|
  c.option('--options=STRING', String) do |opts|
    puts "Parsed mount options: #{opts}"
  end
  c.action do
    puts 'Success'
  end
end

When I run the program, it interprets -vers=3.0 as the global option -v:

$ ruby test.rb --options "-vers=3.0"
Name 0.1

If I use = to separate --options from its argument, it works as expected:

$ ruby test.rb --options="-vers=3.0"
Parsed mount options: -vers=3.0
Success

So, we have a workaround, but I still argue that it's a bug. First, it's very common to omit the =.
Second, it works in either case using optparse directly. Here is an example:

require 'optparse'

OptionParser.new do |opts|
  opts.banner = "Usage: example.rb [options]"

  opts.on("-v", "--version", "Show version") do |v|
    puts "-v received"
  end

  opts.on("-o OPTIONS", "--options OPTIONS", "Provide mount options") do |opts|
    puts "-o received with #{opts}"
  end

end.parse!

Observe that it works as expected in either case:

$ ruby test2.rb --options "-vers=3.0"
-o received with -vers=3.0

$ ruby test2.rb --options="-vers=3.0"
-o received with -vers=3.0

Able to access options set by global_option() outside of the command block

From @pruan-rht on February 16, 2015 18:0

Sometime, it's useful to be able to access options that at set in the global_option() be accessible outside of the command block so that I can do initialization of codes that are global to all sub commands. For example

global_option('-p', '--plan_id PLAN_ID_OR_NAME', 'specify the plan name or plan id')

command :rename do |c|
...
c.action do |arg, options|
...

I want to check the option plan_id outside of the c.action block for the various commands that this global_option applies to

Copied from original issue: tj/commander#94

Integration with Aruba in_process

Imagine the following case:

I'm using aruba in my cucumber BDD tests, which by default is executing the commands in a child process.
That is slow and doesn't allow me to stub/mock components. So the alternative which they provide is called in_process, which is not spawning a new process, it uses the current one .
With this approach you have to trigger your application, since my application is CLI app.
So in my case I have the following:

runner.rb

module Cli
  module App
    class Runner
      def initialize(argv, stdin, stdout, stderr, kernel)
        @argv   = argv
        @stdin  = stdin
        @stdout = stdout
        @stderr = stderr
        @kernel = kernel
      end

      def execute!
        MyApp::Command.new.run
      end
    end
  end
end

And my command.rb looks lik:

require 'commander'

module MyApp
  class Command
    include Commander::Methods

    def run
      program :name, 'myapp'
      program :version, MyApp::VERSION
      program :description, 'This tool handles the syncing from Bitbucket / GIT to your clearcase view.'
      default_command :sync

      global_option('-u', '--url STRING', 'Specifies the link to the third party site')

      command :sync do |cmd|
        cmd.syntax = 'sync [options]'
        cmd.description = 'sync to third party'
        cmd.action do |_args, options|

          if !options.abort
            fail 'The URL, the branch and the stream is mandatory' unless options.stream && options.url && options.branch
          else
            fail 'The stream is mandatory for an abort' unless options.stream
          end

          # Some code executed by my application
        end
      end

      run!
    end
  end
end

And my cucumber step is:
I run myapp sync -u http://example.com

With that my sync command is called, but the options are missing, probably I have to inject them somehow

For example thor gem allows me to do something like that: MyCli::App.start(@argv) in the runner and everything works

So my concern is: how can I inject the options with in my runner ?

post-argument-parsing hook

From @smackesey on October 16, 2014 15:43

Is there any way to do some processing (add to options object, set instance variables and so on) after all options have been parsed but before any command has been run? If not, this would be a useful feature (I'd be willing to implement it in a PR)-- if it exists, then I think it should be mentioned in the README.

Copied from original issue: tj/commander#81

Script ignores small switch

I have specified small switches, so they would show in help and so I have "power" over them. But when I try to use myscript run -t alfa, the type doesnt set. It works for -i and -m just fine. But with type I need to write --type

#!/usr/bin/env ruby

require 'rubygems'
require 'commander/import'

program :name, 'MyScript'
program :version, '0.1.0'
program :description, "Script execution"

command :run do |c|
  c.syntax = 'test_runner run [options]'
  c.description = 'Run test script or a set of test scripts'
  c.option('-t STRING', '--type STRING', String, 'Type')
  c.option('-i STRING', '--input STRING', String, 'Path')
  c.option('-m STRING', '--max_depth STRING', String, 'Maximal depth')
  c.action do |args, options|
    options.default max_depth: '0'
    say "Params are: #{options.type}, #{options.input}, #{options.max_depth}"
  end
end

c.option('-t', '--type STRING', String Does not work either.

Command pipeline and short command sequences support

Hello!

Is there any solution to use command sequences in a short way? For example:

my_gem_based_on_commander cmd1 [some_args] && cmd2 [some_other_args]

instead of

my_gem_based_on_commander cmd1 [some_args] && my_gem_based_on_commander cmd2 [some_other_args]

Also is there any way to support command pipeline (passing results from one command to another like in macos's bash) ? For example:

my_gem_based_on_commander cmd1 [some_args] | cmd2 [some_other_args]

Option parsing error (incomplete option name)

From @d-ash on February 14, 2015 8:40

Passing only a beginning of a name triggers the option to be set.

For example, if --vagrant option is declared, passing --v or --va... to the script, works as if the full name has been passed.

However, global options work correctly (--v is not equivalent to --vagrant)

Copied from original issue: tj/commander#93

Deprecation warnings in Ruby 2.4

Ruby 2.4 throws deprecation errors when running Commander:

/2.4.0/gems/commander-4.4.2/lib/commander/user_interaction.rb:333: warning: constant ::NIL is deprecated
/2.4.0/gems/commander-4.4.2/lib/commander/user_interaction.rb:333: warning: constant ::TRUE is deprecated
/2.4.0/gems/commander-4.4.2/lib/commander/user_interaction.rb:333: warning: constant ::FALSE is deprecated
/2.4.0/gems/commander-4.4.2/lib/commander/user_interaction.rb:333: warning: constant ::Fixnum is deprecated
/2.4.0/gems/commander-4.4.2/lib/commander/user_interaction.rb:333: warning: constant ::Bignum is deprecated

The relevant parts of the docs are here:
https://ruby-doc.org/core-2.4.0/Object.html

Constant depreciation error with Ruby 2.3.0

Ruby 2.2.3 does not give this error so my guess is that 2.3.0 isn't supported yet. Is this something that can be resolved as an issue?

.rvm/gems/ruby-2.3.0/gems/commander-4.3.5/lib/commander/user_interaction.rb:334: warning: constant ::TimeoutError is deprecated

Thanks for your help.
Lewis

--

Double dash as described in tj/commander#34, seems to be broken after 4.4.0, specifically after 420ffa9.

The mentioned example yields -x as an option rather than argument, when used in conjunction with double dash.

Additionally, it fails to parse EG. ruby test.rb foo -- --foo.

Broken compact help formatter

With the most recent version, the help formatter is broken, resulting the following stack trace:

./bin/fastlane --help
(erb):2:in `get_binding': undefined method `size' for nil:NilClass (NoMethodError)
        from /Users/felixkrause/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/erb.rb:849:in `eval'
        from /Users/felixkrause/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/erb.rb:849:in `result'
        from /Users/felixkrause/.rvm/gems/ruby-2.0.0-p481@global/gems/commander-4.3.3/lib/commander/help_formatters/terminal.rb:7:in `render'
        from /Users/felixkrause/.rvm/gems/ruby-2.0.0-p481@global/gems/commander-4.3.3/lib/commander/runner.rb:302:in `block (2 levels) in create_default_commands'
        from /Users/felixkrause/.rvm/gems/ruby-2.0.0-p481@global/gems/commander-4.3.3/lib/commander/command.rb:178:in `call'
        from /Users/felixkrause/.rvm/gems/ruby-2.0.0-p481@global/gems/commander-4.3.3/lib/commander/command.rb:178:in `call'
        from /Users/felixkrause/.rvm/gems/ruby-2.0.0-p481@global/gems/commander-4.3.3/lib/commander/command.rb:153:in `run'
        from /Users/felixkrause/.rvm/gems/ruby-2.0.0-p481@global/gems/commander-4.3.3/lib/commander/runner.rb:57:in `block in run!'
        from /Users/felixkrause/.rvm/gems/ruby-2.0.0-p481@global/gems/commander-4.3.3/lib/commander/runner.rb:382:in `block in global_option_proc'
        from /Users/felixkrause/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/optparse.rb:1364:in `call'
        from /Users/felixkrause/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/optparse.rb:1364:in `block in parse_in_order'
        from /Users/felixkrause/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/optparse.rb:1351:in `catch'
        from /Users/felixkrause/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/optparse.rb:1351:in `parse_in_order'
        from /Users/felixkrause/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/optparse.rb:1345:in `order!'
        from /Users/felixkrause/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/optparse.rb:1437:in `permute!'
        from /Users/felixkrause/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/optparse.rb:1459:in `parse!'
        from /Users/felixkrause/.rvm/gems/ruby-2.0.0-p481@global/gems/commander-4.3.3/lib/commander/runner.rb:363:in `parse_global_options'
        from /Users/felixkrause/.rvm/gems/ruby-2.0.0-p481@global/gems/commander-4.3.3/lib/commander/runner.rb:65:in `run!'
        from /Users/felixkrause/.rvm/gems/ruby-2.0.0-p481@global/gems/commander-4.3.3/lib/commander/delegates.rb:15:in `run!'
        from ./bin/fastlane:118:in `run'
        from ./bin/fastlane:124:in `<main>'

This can be reproduced using the fastlane gem. When I remove the help formatter specification it's working just fine.

Read options.default from anothe class?

Maybe that's a dumb question, but firs of all take a look at this minimal working example:

#!/usr/bin/env ruby

module Minimal
    require 'rubygems'
    require 'commander/import'
    class CLI
        include Commander::Methods
        def self.parse(args)
            program :name, 'MWE'
            program :version, '0.0.1'
            program :description, 'Minimal Working Example'

            command :hello do |c|
                c.syntax = 'mwe hello'
                c.description =  'Say hello to everyone'
                c.option '--spanish', 'Say Hello in Spanish'
                c.option '--french', 'Say Hello in French'
                c.action do |args, options|
                    say options.default
                    options.default
                end
            end
        end
    end
end

module Minimal
    class Working
        def self.example(args)
            option = CLI.parse args
            if option.name = 'hello'
                #=> How can I check if spanish of french option is true?
            end
        end
    end
end

Minimal::Working.example ARGV

Well actually I'm trying to acces to options.default value outside the Commander space object, but I failed. What am I missing?

Thanks in advance

Problem with the width in console? (short lines)

I worked on the old version of commander. (the one from before the transfer to commander-rb)
When I did the update, I notice this strange view: (short lines)

LOL

What's wrong??? Maybe someone knows?

deprecation warning about MissingSourceFile that is being thrown when commander v4.4.0 is used with ActiveSupport v5+

Commander gem shows DEPRECATION WARNING: MissingSourceFile is deprecated! Use LoadError instead. warning when we use commander v4.4.0 with ActiveSupport v5+.

warning is thrown from /lib/commander/user_interaction.rb while executing

 module AskForClass
      # All special cases in HighLine::Question#convert, except those that implement #parse
      (
        [Float, Integer, String, Symbol, Regexp, Array, File, Pathname] +
        # All Classes that respond to #parse
        Object.constants.map do |const|
          # Ignore constants that trigger deprecation warnings
          Object.const_get(const) unless [:Config, :TimeoutError].include?(const)
        end.select do |const|
          const.class == Class && const.respond_to?(:parse)
        end
      ).each do |klass|
        define_method "ask_for_#{klass.to_s.downcase}" do |prompt|
          $terminal.ask(prompt, klass)
        end
      end
    end

which defines the deprecation_message method for the ActiveSupport::Deprecation::Reporting module lib/active_support/deprecation/reporting.rb while executing

       def deprecation_message(callstack, message = nil)
          message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
          "DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}"
        end

could you please fix this issue.

How to parse boolean switches

I would like to run a command that works like this:

command --foo true --bar true

but look like this:

command --foo --bar

Is this possible?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.