Ruby cli

From wikinotes

Some rough starting points for creating commandline interfaces in ruby.
salt to taste.

Param Parsing, Raw

no structure

#!/usr/bin/env ruby

EXECUTABLE = File.basename(__FILE__)

name = "unknown"
age = "unknown"
shift = 0
ARGV.count.times do |index|
  if shift > 0
    shift -= 1
    next
  end

  case ARGV[index]
  when '-h', '--help'
    helpmsg = <<~HELP
    #{EXECUTABLE} [-h] [-n NAME] [-a AGE]

    DESCRIPTION:
      says hello

    PARAMS:
      -n --name:
        assign a name

      -a --age:
        assign an age

    EXAMPLE:
      #{EXECUTABLE} -n my-name -a 30

    HELP
    puts(helpmsg)
    exit(0)

  when '-n', '--name'
    name = ARGV[index+1]
    shift += 1

  when '-a', '--age'
    age = ARGV[index+1]
    shift += 1

  else
    puts "error"
    exit(1)

  end

end

puts("hello, #{name} with age #{age}")

enumerator structure


#!/usr/bin/env ruby

EXECUTABLE = File.basename(__FILE__)


class ArgumentIterator
  def initialize(argv)
    @argv = argv
    @index = 0
  end

  def shift(n = 1)
    n.times.map { next_item }
  end

  def next_item
    val = @argv[@index]
    @index += 1
    val
  end

  def each
    Enumerator.new do |enum|
      loop do
        break if @index >= @argv.count

        enum.yield([self, next_item])
      end
    end.each { |this, val| yield(this, val) }
  end
end


class CommandlineInterface
  Args = Struct.new(:name, :age)

  def help_msg
    <<~HELP
    #{EXECUTABLE} [-h] [-n NAME] [-a AGE]

    DESCRIPTION:
      says hello

    PARAMS:
      -n --name:
        assign a name

      -a --age:
        assign an age

    EXAMPLE:
      #{EXECUTABLE} -n my-name -a 30

    HELP
  end

  def parse_args(argv)
    name = "unknown"
    age = "unknown"

    ArgumentIterator.new(argv).each do |iterator, arg|
      case arg
      when '-h', '--help'
        puts(help_msg)
        exit(0)

      when '-n', '--name'
        name, = iterator.shift

      when '-a', '--age'
        age, = iterator.shift

      else
        $stderr.puts("[ERROR] unexpected argument #{arg}")
        exit(1)

      end
    end

    Args.new(name, age)
  end
end


cli = CommandlineInterface.new
args = cli.parse_args(ARGV)
puts("hello, #{args.name} with age #{args.age}")

Optparse

Ruby ships also ships with ruby optparse.
It works by defining a DSL for params (ex. opts.on("-n NAME", "--name NAME"), and defining callbacks for each param.
I think it's a bit more complicated than it needs to be.

Example from the docs:

require 'optparse'

options = {}
OptionParser.new do |parser|
  parser.on("-r", "--require LIBRARY",
            "Require the LIBRARY before executing your script") do |lib|
    puts "You required #{lib}!"
  end
end.parse!