How do I get only long options work in OptionParser in Ruby?

2.2k views Asked by At

I have such a simple code in Ruby (test.rb):

#! /usr/bin/env ruby

require 'optparse'

OptionParser.new do |option|
  option.on("--sort", "Sort data") do 
    puts "--sort passed"
  end
end.parse!

then I run it: ./test.rb -s and got:

--sort passed

Have I missed something?

I want the only --sort (long) option works, not the short one.

How do I get it?

5

There are 5 answers

1
Stefan On

My program has a parameter '--sort' which may accept arguments like '-s', 's' , '+s', etc

In that case you can pass an array of valid arguments to your option:

require 'optparse'

OptionParser.new do |option|
  option.on("--sort TYPE", %w(-s s +s), "Sort data") do |type|
    puts "--sort passed with argument #{type}"
  end
end.parse!

Usage:

$ ./test.rb --sort -s
--sort passed with argument -s
$ ./test.rb --sort s
--sort passed with argument s
$ ./test.rb --sort +s
--sort passed with argument +s

Note that you can still use the shorthand -s:

$ ./test.rb -s -s
--sort passed with argument -s
$ ./test.rb -s s
--sort passed with argument s
$ ./test.rb -s +s
--sort passed with argument +s
1
Jon Cairns On

From the documentation, it appears that this isn't possible.

The #on method uses the syntax of #make_switch, which is described here. The whole documentation makes no mention of being able to turn long or short variables on or off.

However, is this really a problem? The convention is that options are accessible via long and short names, and forcing a change in that behaviour might frustrate your users.

If you really don't want to allow short names, the best option would be to look at some other libraries (e.g. highline, slop, trollop) or roll your own.

0
Alex D On

I found the code which causes this behavior, in optparse.rb, lines 1378-1380:

# if no short options match, try completion with long
# options.
sw, = complete(:long, opt)

If you don't like that behavior, it seems your best option is to create a copy of optparse.rb within your project, remove the offending rescue InvalidOption clause within the copy, and load that rather than the standard library's version.

0
Малъ Скрылевъ On

It is interesting behaviour that if you define the similar long option that begins with the same letter, in the example is s. It does not allow to use -s key with exception OptionParser::AmbiguousOption, but it seems that there no a way to disable the short option for OptionParser without invading into its code:

#! /usr/bin/env ruby

require 'optparse'

OptionParser.new do |option|
  option.on("--sport", "Sport data") do
    puts "--sport passed"
  end
  option.on("--sort", "Sort data") do
    puts "--sort passed"
  end
end.parse!

This is the expanded version of on method:

OptionParser.new do |option|
  opts = [ "--sort", "Sort data" ]
  sw = option.make_switch(opts) 
  block = proc { puts "--sort passed" }
  sw[0].instance_variable_set :@block, block
  option.top.append *sw
  p sw
  # => [#<OptionParser::Switch::NoArgument:0x806c770 @pattern=/.*/m, @conv=#<Proc:0x806dd8c@/home/malo/.rvm/rubies/ruby-1.9.3-p448/lib/ruby/1.9.1/optparse.rb:1617>, @short=[], @long=["--sort"], @arg=nil, @desc=["Sort data"], @block=#<Proc:0x806c70c@./1.rb:8>>, [], ["sort"], nil, []]
end.parse!
# => --sort passed when  ./1.rb --sort and ./1.rb -s

It is interesting that @short variable is empty but the app reacts on -s key.

I would prefer to use micro-optparse gem. Use it as follows:

Gemfile

gem 'micro-optparse', :git => 'https://github.com/3aHyga/micro-optparse.git', :branch => 'no-short' # for now it is available only from git repo

ruby_script.rb

require 'micro-optparse'

options = Parser.new do |p|
   p.banner = "This is a fancy script, for usage see below"
   p.option :sport, "sport", :default => "Sport", :short => "p"
   p.option :sort, "sort", :default => "Sort", :short => false
end.process!

p options

Simulation:

$ bundle exec ./1.rb --sort 111
{:sport=>"Sport", :sort=>"111"}

$ bundle exec ./1.rb -s 111
ambiguous option: -s

$ bundle exec ./1.rb -p 111
{:sport=>"111", :sort=>"Sort"}
0
Sam Saffron On

You can reopen OptionParser::OptionMap to disable completions with:

class OptionParser::OptionMap
  def complete(key, icase = false, pat = nil)
    # disable completions
    nil
  end
end

This will disable the predefined behavior of searching for stuff to complete.