Printing list of only some options using Ruby OptionParser - ruby

My program has a lot of command line options. But I don't want to overwhelm the user when he types --help. Instead of printing out all options, I'd like to list only the most important ones, printing the rest of them when one types --help=full.
But OptionParser seems to only support printing of the whole list of defined options. I'd like to write my own code that loops over the defined options and prints them out in my way, but OptionParser doesn't seem to provide any ways to access the options definitions it contains.
Is there a way to access the options in OptionParser that I may have missed? Or is there some good alternative to OptionParser? Or some other approach to this problem?

You could redefine the option --help for your need.
require 'optparse'
#create parsers
opts = OptionParser.new()
opts.banner = "Usage: example.rb [options]"
opts.separator("test optparse with --help[=full]")
opts.on("-v", "--[no-]verbose", "Run verbosely") { |v|
puts "->Verbose ist #{v.inspect}"
}
opts.on("-r", "--repeat REPEAT", "Repeat REPEAT times") { |v|
puts "->Repeat ist #{v.inspect}"
}
#Define your own --help
opts.on("-h", "--help [HELP]", "Help") { |v|
case v
when 'full' #write original help
puts opts.help
when nil, '' #write script specific help
puts opts.banner
opts.summarize([], opts.summary_width ) { |helpline|
#make your own decision on each helpline
#~ puts helpline #puts each line
puts helpline unless helpline =~ /-v/ #ignore -v
}
else
puts opts.banner
puts <<helpmessage
Undefined --help option. Please use 'full' or no option
#{File.basename(__FILE__)} --help
#{File.basename(__FILE__)} --help=full
helpmessage
end
}
opts.parse!
In this version, --help shows all options, but not -v. You may make your own selection - or write a complete different help.

Related

How to get the specified option flag from within OptionParser

I'd like to get the exact option flag that was specified on the command line from within Ruby's OptionParser.
For example, suppose I have the following code:
parser = OptionParser.new {
|opts|
opts.on('-f', '--file FILE', 'filename') {
|arg|
$filename = arg
# Here I'd like to know whether '-f' or '--file' was entered
# on the command line.
}
# ... etc. ...
}
I'd like to know whether the user happened to type '-f' or '--file' on the command line. Is this possible without writing two separate opts.on blocks?
I don't think you can get the flags being passed in when inside the OptionParser.new block. At that point it's too late. However, prior to OptionParser parsing the command-line, it's possible to look and see what's being passed in.
ARGV contains the raw command-line. For instance, if this is the command-line invocation for some code:
foo -i 1 -j 2
then ARGV will contain:
["-i", "1", "-j", "2"]
and, then it becomes pretty easy to grab the flags:
ARGV.grep(/^-/) # => ["-i", "-j"]
There are other OptionParser-like tools for Ruby, and those might let you access the flags being used, but I can't think of a reason I'd ever care to. Looking at your code it seems like you're not understanding how to use OptionParser:
parser = OptionParser.new {
|opts|
opts.on('-f', '--file FILE', 'filename') {
|arg|
$filename = arg
# Here I'd like to know whether '-f' or '--file' was entered
# on the command line.
}
# ... etc. ...
}
Instead of doing it that way, I'd write it:
options = {}
OptionParser.new do |opts|
opts.on('-f', '--file FILE', 'filename') { |arg| options[:filename] = arg }
end.parse!
if options[:filename]
puts 'exists' if File.exist?(options[:filename])
end
Then, later in your code you can check in the options hash to see if either of the -f or --file options was given, and what the value was. That it was one or the other of -f or --file shouldn't ever matter.
If it does then you need to differentiate between the two flags, instead of treating them as if they're aliases:
options = {}
OptionParser.new do |opts|
opts.on('-f', 'filename') { |arg| options[:f] = arg }
opts.on('--file FILE', 'filename') { |arg| options[:file] = arg }
end.parse!
if options[:file] || options[:f]
puts 'exists' if File.exist?(options[:file] || options[:f])
end

Optparse doesn't seem to return ARGV array. Argument Required Error

This is homework and I do not expect you to give me the complete answer.
I'm trying to parse a command line entry such as:
./apacheReport.rb -u testlog.txt
When I enter this:
./apacheReport.rb -u testlog.txt
I get:
Argument required
My code is:
require_relative 'CommonLog'
require 'optparse'
# puts ARGV.inspect
optparser = OptionParser.new
optU = false
optI = false
optS = false
optH = false
optparser.banner = "apacheReport.rb [options] filename"
optparser.parse!
rescue => m
puts m.message
puts optparser
exit
end
if ARGV.length < 1
puts "Argument required"
exit
end
userInputFile = ARGV[0]
userInputFile.to_s
file = CommonLog.new(userInputFile)
It should parse the leftover portion of the command into ARGV[0] then should store it as userInputFile and then create a CommonLog object using the file as the constructor. At that point I call the methods that were specified in the command.
It seems that for some reason my ARGV is not being returned. I'm not sure what the issue is.
Ruby's OptionParser is easy to use, but you have to puzzle through the documentation. Here's a little example that'd be useful for your code:
require 'optparse'
options = {}
OptionParser.new do |opt|
opt.on('-u', '--use_this FILE', 'Use this file') { |o| options[:use_this] = o }
end.parse!
options will contain the flags. In this case, if you pass in -u foo, options[:use_this] will be foo.
Save that and try running it without and with a parameter. Also try running it with just a -h flag.
You can search StackOverflow for more answers where I was dealing with OptionParser.
It's hard to tell what's wrong since you code doesn't seem to be working at the moment. The problem may be that the parse! method removes found options from ARGV. So when you write:
optparser.parse!
It removes your two parameters (-u testlog.txt) and this code always fails:
if ARGV.length < 1
puts "Argument required"
exit
end
Instead of looking at ARGV, you need to set up optparser correctly. Perhaps something like:
optparser = OptionParser.new do |opts|
opts.banner = "apacheReport.rb [options] filename"
opts.on("-u", "--u-short-for-this", "Whatever u stands for") do |u|
optU = u
end
end
Then optU will be true only if the user passed -u and the filename will be in ARGV[0].

How to generate OptionParser require arguments

The code below works, but I am manually raising argument errors for the required arguments using fetch, when I want to build the required arguments into the native OptionParser sytax for required parameters:
# ocra script.rb -- --type=value
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on("--type [TYPE]",String, [:gl, :time], "Select Exception file type (gl, time)") do |t|
options["type"] = t
end
opts.on("--company [TYPE]",String, [:jaxon, :doric], "Select Company (jaxon, doric)") do |t|
options["company"] = t
end
end.parse!
opts = {}
opts['type'] = options.fetch('type') do
raise ArgumentError,"no 'type' option specified as a parameter (gl or time)"
end
opts['company'] = options.fetch('company') do
raise ArgumentError,"no 'company' option specified as a parameter (doric or jaxon)"
end
There's a similar question with an answer that may help you:
"How do you specify a required switch (not argument) with Ruby OptionParser?"
In short: there doesn't seem to be a way to make an option required (they are called options after all).
There is an OptionParser::MissingArgument exception that you could raise rather than the ArgumentError you're currently throwing.
Faced with the same situation, I ended up with an option like this. If not all of my mandatory options are provided, output the user-friendly help text generated by OptionParser based on my defined options. Feels cleaner than throwing an exception and printing a stack trace to the user.
options = {}
option_parser = OptionParser.new do |opts|
opts.banner = "Usage: #{$0} --data-dir DATA_DIR [options]"
# A non-mandatory option
opts.on('-p', '--port PORT', Integer, 'Override port number') do |v|
options[:port] = v
end
# My mandatory option
opts.on('-d', '--data-dir DATA_DIR', '[Mandatory] Specify the path to the data dir.') do |d|
options[:data_dir] = d
end
end
option_parser.parse!
if options[:data_dir].nil?
puts option_parser.help
exit 1
end

OptionParser returning bool instead of argument?

When I run this sample from the OptionParser documentation:
require 'optparse'
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
options[:verbose] = v
end
end.parse!
p options
p ARGV
and type: ruby test.rb -v 100, it returns:
{:verbose=>true}
["100"]
Shouldn't verbose be 100, not a boolean?
I have no idea about this, does anyone have any advice?
You've specified that the -v option does not have an argument:
opts.on("-v", ...
If you want it to take an argument then you have to say so:
opts.on("-v n", "--verbose=n", ...
#-----------^
And if you want to force n to be an integer, then:
opts.on('-v n', '--verbose=n', OptionParser::DecimalInteger, ...
You want to start reading at the make_switch docs (such as it is) and then reverse engineer the examples.
Don't feel bad about being confused, the OptionParser documentation isn't quite the best thing ever.

Extracting filenames from command line arguments with Ruby

I'm trying to use optparse to parse command line arguments. I would like my program to accept arguments like that:
$ ./myscript.rb [options] filename
I can easily manage the [options] part:
require 'optparse'
options = { :verbose => false, :type => :html }
opts = OptionParser.new do |opts|
opts.on('-v', '--verbose') do
options[:verbose] = true
end
opts.on('-t', '--type', [:html, :css]) do |type|
options[:type] = type
end
end
opts.parse!(ARGV)
But how do I get the filename?
I could extract it manually from ARGV, but there has to be a better solution, just can't figure out how
The "parse" method returns the unprocessed ARGV. So in your example, it will return a one element array with the filename in it.
I can't just use ARGV.pop. For example
when the last argument is "css" it
could either be a file or belong to
--type switch.
But if your script requires the last argument to be a filename (which is what your usage output inquires) this case should never happen the script should exit with a non-zero and the user should get a usage report or error.
Now if you want to make a default filename or not require a filename as the last argument but leave it optional then you could just test to see if the last argument is a valid file. If so use it as expected otherwise continue without etc.
Hope this answer can still be useful.
Ruby has one built-in variable __FILE__ can do this type of work.
puts __FILE__
it will print out your file's name.
I don't think extracting it before sending it to OptionParser is bad, I think it makes sense. I probably say this because I have never used OptionParser before, but oh well.
require 'optparse'
file = ARGV.pop
opts = OptionParser.new do |opts|
# ...
end

Resources