Ruby cli: Difference between revisions

From wikinotes
 
(11 intermediate revisions by the same user not shown)
Line 1: Line 1:
Some rough starting points for creating commandline interfaces in ruby.<br>
salt to taste.


= Param Parsing, Raw =
= Param Parsing, Raw =
<blockquote>
<blockquote>
{{ collapse
{{ expand
| no structure
| no structure
|
|
Line 22: Line 24:
   when '-h', '--help'
   when '-h', '--help'
     helpmsg = <<~HELP
     helpmsg = <<~HELP
     #{EXECUTABLE} [-h]
     #{EXECUTABLE} [-h] [-n NAME] [-a AGE]


     DESCRIPTION:
     DESCRIPTION:
Line 30: Line 32:
       -n --name:
       -n --name:
         assign a name
         assign a name
      -a --age:
        assign an age


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


     HELP
     HELP
Line 55: Line 60:


puts("hello, #{name} with age #{age}")
puts("hello, #{name} with age #{age}")
</syntaxhighlight>
}}
{{ expand
| enumerator structure
|
<syntaxhighlight lang="ruby">
#!/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}")
</syntaxhighlight>
</syntaxhighlight>
}}
}}
</blockquote><!-- Param Parsing -->
</blockquote><!-- Param Parsing -->
= Optparse =
<blockquote>
Ruby ships also ships with [[ruby optparse]].<br>
It works by defining a DSL for params (ex. <code>opts.on("-n NAME", "--name NAME"</code>), and defining callbacks for each param.<br>
I think it's a bit more complicated than it needs to be.
Example from the docs:
<syntaxhighlight lang="ruby">
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!
</syntaxhighlight>
</blockquote><!-- Optparse -->

Latest revision as of 16:44, 29 October 2022

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!