Access the command line option literals in a Ruby script with OptParser

197 Views Asked by At

I would like to get access to the literal text of the command-line options passed to a Ruby script, before parsing.

Here's an example (just a simplified example to make the question clear—this is not "my code") that will make this clearer:

require 'optparse'

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

  opts.on("-s", "--start", "Enter start date for the data you want from the API") do |start_date|
    options[:start_date] = start_date
  end
  opts.on("-e", "--end", "Enter the end date for the data you want from the API") do |end_date|
    options[:end_date] = end_date
  end
  opts.on("-w", "--window", "Enter a number of days back in time to look prior to today") do |window|
    options[:start_date] = window.days.ago
    options[:end_date] = Time.zone.today
  end
  opts.on("-a", "--all", "Get all historical data since the beginning of time (as recorded in the API's database)") do |all_time|
    options[:start_date] = Date.parse("1900-01-01")
    options[:end_date] = Time.zone.today
  end
  raise "You can't define an end date and not a start date" if command_line_options & %w[-e --end] && !(command_line_options & %w[-s --start])
  raise "Please use only one type of temporal option." if (command_line_options & %w[-a --all -w --window -e --end]).count > 1
end.parse!

The raise lines here show what I'm trying to accomplish. Take a look at command_line_options on those lines. In the example script, those have no value. How can I set them?

Notice that whether the user uses --start and --end or --window or --all, the option[:start_date] and option[:end_date] are being set. I've offered the user 3 different ways to set the start and end dates, but I want to make sure they don't mix and match those. But in order to do this input validation and make sure the user is entering sensible values, I want to have access to the raw unparsed command-line switches. How can I do this?

I realize there are other ways I can accomplish my goal, but what I'm describing seems like it ought to be easily possible, and it seems like the cleanest way to do things.

1

There are 1 best solutions below

0
Aleksei Matiushkin On

This is not possible. Also, your code has too many glitches to worry about the best way to accomplish the wrong input reporting.

  1. all the argument come as strings
  2. has no method Time.zone
  3. parameterized options are to be specified with a parameter inplace
  4. does not declare method days on strings (neither on integers,) it’s all crap

To sum it up, here is the working version.

require 'optparse'
require 'date'

options = Hash.new { |h, k| h[k] = [] }
OptionParser.new do |opts|
  opts.banner = "Usage: example.rb [options]"

  opts.on("-sSTART", "--start=START", "Enter start date for the data you want from the API") do |start_date|
    options[:start_date] << Date.parse(start_date)
  end
  opts.on("-eEND", "--end=END", "Enter the end date for the data you want from the API") do |end_date|
    options[:end_date] << Date.parse(end_date)
  end
  opts.on("-wWINDOW", "--window=WINDOW", "Enter a number of days back in time to look prior to today") do |window|
    options[:start_date] << Date.today - Integer(window)
    options[:end_date] << Date.today
  end
  opts.on("-aALL", "--all=ALL", "Get all historical data since the beginning of time (as recorded in the API's database)") do |all_time|
    options[:start_date] << Date.parse("1900-01-01")
    options[:end_date] << Date.today
  end
end.parse!

case options.map(&:last).map(&:size)
when [1, 1] then :ok
when [], [1] then raise "You must define both dates"
else raise "Please use only one type of temporal option"
end

options = options.map { |k, v| [k, v.first] }.to_h
puts options