Class: OptionParser

Class | OptionParser
In: | optparse.rb
Parent: | Object

## OptionParser

### Introduction

OptionParser is a class for command-line option analysis. It is much more advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented solution.

### Features

1. The argument specification and the code to handle it are written in the same place.
2. It can output an option summary; you don’t need to maintain this string separately.
3. Optional and mandatory arguments are specified very gracefully.
4. Arguments can be automatically converted to a specified class.
5. Arguments can be restricted to a certain set.

All of these features are demonstrated in the example below.

### Example

The following example is a complete Ruby program. You can run it and see the effect of specifying various options. This is probably the best way to learn the features of `optparse`.

      require 'optparse'
      require 'optparse/time'
      require 'ostruct'
      require 'pp'

      class OptparseExample

        CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
        CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }

        #
        # Return a structure describing the options.
        #
        def self.parse(args)
          # The options specified on the command line will be collected in *options*.
          # We set default values here.
          options = OpenStruct.new
          options.library = []
          options.inplace = false
          options.encoding = "utf8"
          options.transfer_type = :auto
          options.verbose = false

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

            opts.separator ""
            opts.separator "Specific options:"

            # Mandatory argument.
            opts.on("-r", "--require LIBRARY",
                    "Require the LIBRARY before executing your script") do |lib|
              options.library << lib
            end

            # Optional argument; multi-line description.
            opts.on("-i", "--inplace [EXTENSION]",
                    "Edit ARGV files in place",
                    "  (make backup if EXTENSION supplied)") do |ext|
              options.inplace = true
              options.extension = ext || ''
              options.extension.sub!(/\A\.?(?=.)/, ".")  # Ensure extension begins with dot.
            end

            # Cast 'delay' argument to a Float.
            opts.on("--delay N", Float, "Delay N seconds before executing") do |n|
              options.delay = n
            end

            # Cast 'time' argument to a Time object.
            opts.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
              options.time = time
            end

            # Cast to octal integer.
            opts.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger,
                    "Specify record separator (default \\0)") do |rs|
              options.record_separator = rs
            end

            # List of arguments.
            opts.on("--list x,y,z", Array, "Example 'list' of arguments") do |list|
              options.list = list
            end

            # Keyword completion.  We are specifying a specific set of arguments (CODES
            # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
            # the shortest unambiguous text.
            code_list = (CODE_ALIASES.keys + CODES).join(',')
            opts.on("--code CODE", CODES, CODE_ALIASES, "Select encoding",
                    "  (#{code_list})") do |encoding|
              options.encoding = encoding
            end

            # Optional argument with keyword completion.
            opts.on("--type [TYPE]", [:text, :binary, :auto],
                    "Select transfer type (text, binary, auto)") do |t|
              options.transfer_type = t
            end

            # Boolean switch.
            opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
              options.verbose = v
            end

            opts.separator ""
            opts.separator "Common options:"

            # No argument, shows at tail.  This will print an options summary.
            # Try it and see!
            opts.on_tail("-h", "--help", "Show this message") do
              puts opts
              exit
            end

            # Another typical switch to print the version.
            opts.on_tail("--version", "Show version") do
              puts OptionParser::Version.join('.')
              exit
            end
          end

          opts.parse!(args)
          options
        end  # parse()

      end  # class OptparseExample

      options = OptparseExample.parse(ARGV)
      pp options

Note: some bugs were fixed between 1.8.0 and 1.8.1. If you experience trouble with the above code, keep this in mind.

### Further documentation

The methods are not individually documented at this stage. The above example should be enough to learn how to use this class. If you have any questions, email me (gsinclair@soyabean.com.au) and I will update this document.

### Methods

abort accept accept add_officious banner base def_head_option def_option def_tail_option define define_head define_tail each_const environment help inc inc load make_switch new new on on_head on_tail order order! parse parse! permute permute! program_name reject reject release remove search_const separator show_version summarize terminate terminate to_a to_s top top ver version warn with

### Classes and Modules

Module OptionParser::Acceptables
Module OptionParser::Arguable
Module OptionParser::Completion
Class OptionParser::AmbiguousArgument
Class OptionParser::AmbiguousOption
Class OptionParser::CompletingHash
Class OptionParser::InvalidArgument
Class OptionParser::InvalidOption
Class OptionParser::List
Class OptionParser::MissingArgument
Class OptionParser::NeedlessArgument
Class OptionParser::OptionMap
Class OptionParser::ParseError
Class OptionParser::Switch

### Constants

DecimalInteger | = | /\A[-+]?#{decimal}/io
OctalInteger | = | /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))/io
DecimalNumeric | = | floatpat # decimal integer is allowed as float also.

### External Aliases

banner= | -> | set_banner
| for experimental cascading :-)
program_name= | -> | set_program_name
summary_width= | -> | set_summary_width
summary_indent= | -> | set_summary_indent

### Attributes

banner | [W] |
default_argv | [RW] |
program_name | [W] |
release | [W] |
summary_indent | [RW] |
summary_width | [RW] |
version | [W] |

### Public Class methods

accept(*args, &blk)

[Source]

    # File optparse.rb, line 857
      def self.accept(*args, &blk) top.accept(*args, &blk) end

each_const(path, klass = ::Object)

[Source]

    # File optparse/version.rb, line 49
      def each_const(path, klass = ::Object)
        path.split(/::|\//).inject(klass) do |klass, name|
          raise NameError, path unless Module === klass
          klass.constants.grep(/#{name}/i) do |c|
            klass.const_defined?(c) or next
            c = klass.const_get(c)
          end
        end
      end

inc(arg, default = nil)

[Source]

    # File optparse.rb, line 782
      def self.inc(arg, default = nil)
        case arg
        when Integer
          arg.nonzero?
        when nil
          default.to_i + 1
        end
      end

new(banner = nil, width = 32, indent = ' ' * 4) {|self if block_given?| ...}

[Source]

    # File optparse.rb, line 807
      def initialize(banner = nil, width = 32, indent = ' ' * 4)
        @stack = [DefaultList, List.new, List.new]
        @program_name = nil
        @banner = banner
        @summary_width = width
        @summary_indent = indent
        @default_argv = ARGV
        add_officious
        yield self if block_given?
      end

reject(*args, &blk)

[Source]

    # File optparse.rb, line 868
      def self.reject(*args, &blk) top.reject(*args, &blk) end

search_const(klass, name) {|klass, cname, const if name === cname| ...}

[Source]

    # File optparse/version.rb, line 59
      def search_const(klass, name)
        klasses = [klass]
        while klass = klasses.shift
          klass.constants.each do |cname|
            klass.const_defined?(cname) or next
            const = klass.const_get(cname)
            yield klass, cname, const if name === cname
            klasses << const if Module === const and const != ::Object
          end
        end
      end

show_version(*pkg)

[Source]

    # File optparse/version.rb, line 4
      def show_version(*pkg)
        progname = ARGV.options.program_name
        result = false
        show = proc do |klass, cname, version|
          str = "#{progname}"
          unless klass == ::Object and cname == :VERSION
            version = version.join(".") if Array === version
            str << ": #{klass}" unless klass == Object
            str << " version #{version}"
          end
          [:Release, :RELEASE].find do |rel|
            if klass.const_defined?(rel)
              str << " (#{klass.const_get(rel)})"
            end
          end
          puts str
          result = true
        end
        if pkg.size == 1 and pkg[0] == "all"
          self.search_const(::Object, /\AV(?:ERSION|ersion)\z/) do |klass, cname, version|
            unless cname[1] == ?e and klass.const_defined?(:Version)
              show.call(klass, cname.intern, version)
            end
          end
        else
          pkg.each do |pkg|
            begin
              pkg = pkg.split(/::|\//).inject(::Object) {|m, c| m.const_get(c)}
              v = case
                  when pkg.const_defined?(:Version)
                    pkg.const_get(n = :Version)
                  when pkg.const_defined?(:VERSION)
                    pkg.const_get(n = :VERSION)
                  else
                    n = nil
                    "unknown"
                  end
              show.call(pkg, n, v)
            rescue NameError
            end
          end
        end
        result
      end

terminate(arg = nil)

[Source]

    # File optparse.rb, line 837
      def self.terminate(arg = nil)
        throw :terminate, arg
      end

top()

[Source]

    # File optparse.rb, line 842
      def self.top() DefaultList end

with(*args, &block)

[Source]

    # File optparse.rb, line 771
      def self.with(*args, &block)
        opts = new(*args)
        opts.instance_eval(&block)
        opts
      end

### Public Instance methods

abort(mesg = $!)

[Source]

    # File optparse.rb, line 944
      def abort(mesg = $!)
        super("#{program_name}: #{mesg}")
      end

accept(*args, &blk)

[Source]

    # File optparse.rb, line 856
      def accept(*args, &blk) top.accept(*args, &blk) end

add_officious()

:nodoc:

[Source]

    # File optparse.rb, line 819
      def add_officious
        list = base()
        Officious.each_pair do |opt, block|
          list.long[opt] ||= block.call(self)
        end
      end

banner()

[Source]

    # File optparse.rb, line 897
      def banner
        @banner ||= "Usage: #{program_name} [options]"
      end

base()

[Source]

    # File optparse.rb, line 960
      def base
        @stack[1]
      end

def_head_option(*opts, &block)

Alias for define_head

def_option(*opts, &block)

Alias for define

def_tail_option(*opts, &block)

Alias for define_tail

define(*opts, &block)

[Source]

    # File optparse.rb, line 1211
      def define(*opts, &block)
        top.append(*(sw = make_switch(*opts, &block)))
        sw[0]
      end

define_head(*opts, &block)

[Source]

    # File optparse.rb, line 1221
      def define_head(*opts, &block)
        top.prepend(*(sw = make_switch(*opts, &block)))
        sw[0]
      end

define_tail(*opts, &block)

[Source]

    # File optparse.rb, line 1231
      def define_tail(*opts, &block)
        base.append(*(sw = make_switch(*opts, &block)))
        sw[0]
      end

environment(env = File.basename($0, '.*'))

[Source]

    # File optparse.rb, line 1481
      def environment(env = File.basename($0, '.*'))
        env = ENV[env] || ENV[env.upcase] or return
        parse(*Shellwords.shellwords(env))
      end

help()

[Source]

    # File optparse.rb, line 1011
      def help; summarize(banner.to_s.sub(/\n?\z/, "\n")) end

inc(*args)

[Source]

    # File optparse.rb, line 790
      def inc(*args)
        self.class.inc(*args)
      end

load(filename = nil)

[Source]

    # File optparse.rb, line 1459
      def load(filename = nil)
        begin
          filename ||= File.expand_path(File.basename($0, '.*'), '~/.options')
        rescue
          return false
        end
        begin
          parse(*IO.readlines(filename).each {|s| s.chomp!})
          true
        rescue Errno::ENOENT, Errno::ENOTDIR
          false
        end
      end

make_switch(*opts, &block)

[Source]

    # File optparse.rb, line 1083
      def make_switch(*opts, &block)
        short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
        ldesc, sdesc, desc, arg = [], [], []
        default_style = Switch::NoArgument
        default_pattern = nil
        klass = nil
        o = nil
        n, q, a = nil

        opts.each do |o|
          # argument class
          next if search(:atype, o) do |pat, c|
            klass = notwice(o, klass, 'type')
            if not_style and not_style != Switch::NoArgument
              not_pattern, not_conv = pat, c
            else
              default_pattern, conv = pat, c
            end
          end

          # directly specified pattern(any object possible to match)
          if !(String === o) and o.respond_to?(:match)
            pattern = notwice(o, pattern, 'pattern')
            conv = (pattern.method(:convert).to_proc if pattern.respond_to?(:convert))
            next
          end

          # anything others
          case o
          when Proc, Method
            block = notwice(o, block, 'block')
          when Array, Hash
            case pattern
            when CompletingHash
            when nil
              pattern = CompletingHash.new
              conv = (pattern.method(:convert).to_proc if pattern.respond_to?(:convert))
            else
              raise ArgumentError, "argument pattern given twice"
            end
            o.each {|(o, *v)| pattern[o] = v.fetch(0) {o}}
          when Module
            raise ArgumentError, "unsupported argument type: #{o}"
          when *ArgumentStyle.keys
            style = notwice(ArgumentStyle[o], style, 'style')
          when /^--no-([^\[\]=\s]*)(.+)?/
            q, a = $1, $2
            o = notwice(a ? Object : TrueClass, klass, 'type')
            not_pattern, not_conv = search(:atype, o) unless not_style
            not_style = (not_style || default_style).guess(arg = a) if a
            default_style = Switch::NoArgument
            default_pattern, conv = search(:atype, FalseClass) unless default_pattern
            ldesc << "--no-#{q}"
            long << 'no-' + (q = q.downcase)
            nolong << q
          when /^--\[no-\]([^\[\]=\s]*)(.+)?/
            q, a = $1, $2
            o = notwice(a ? Object : TrueClass, klass, 'type')
            if a
              default_style = default_style.guess(arg = a)
              default_pattern, conv = search(:atype, o) unless default_pattern
            end
            ldesc << "--[no-]#{q}"
            long << (o = q.downcase)
            not_pattern, not_conv = search(:atype, FalseClass) unless not_style
            not_style = Switch::NoArgument
            nolong << 'no-' + o
          when /^--([^\[\]=\s]*)(.+)?/
            q, a = $1, $2
            if a
              o = notwice(NilClass, klass, 'type')
              default_style = default_style.guess(arg = a)
              default_pattern, conv = search(:atype, o) unless default_pattern
            end
            ldesc << "--#{q}"
            long << (o = q.downcase)
          when /^-(\[\^^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
            q, a = $1, $2
            o = notwice(Object, klass, 'type')
            if a
              default_style = default_style.guess(arg = a)
              default_pattern, conv = search(:atype, o) unless default_pattern
            end
            sdesc << "-#{q}"
            short << Regexp.new(q)
          when /^-(.)(.+)?/
            q, a = $1, $2
            if a
              o = notwice(NilClass, klass, 'type')
              default_style = default_style.guess(arg = a)
              default_pattern, conv = search(:atype, o) unless default_pattern
            end
            sdesc << "-#{q}"
            short << q
          when /^=/
            style = notwice(default_style.guess(arg = o), style, 'style')
            default_pattern, conv = search(:atype, Object) unless default_pattern
          else
            desc.push(o)
          end
        end

        default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
        s = if short.empty? and long.empty?
              raise ArgumentError, "no switch given" if style or pattern or block
              desc
            else
              (style || default_style).new(pattern || default_pattern,
                                           conv, sdesc, ldesc, arg, desc, block)
            end
        return s, short, long,
          (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
          nolong
      end

new() {|self| ...}

[Source]

    # File optparse.rb, line 968
      def new
        @stack.push(List.new)
        if block_given?
          yield self
        else
          self
        end
      end

on(*opts, &block)

[Source]

    # File optparse.rb, line 1215
      def on(*opts, &block)
        define(*opts, &block)
        self
      end

on_head(*opts, &block)

[Source]

    # File optparse.rb, line 1225
      def on_head(*opts, &block)
        define_head(*opts, &block)
        self
      end

on_tail(*opts, &block)

[Source]

    # File optparse.rb, line 1235
      def on_tail(*opts, &block)
        define_tail(*opts, &block)
        self
      end

order(*argv, &block)

[Source]

    # File optparse.rb, line 1263
      def order(*argv, &block)
        argv = argv[0].dup if argv.size == 1 and Array === argv[0]
        order!(argv, &block)
      end

order!(argv = default_argv, &nonopt)

[Source]

    # File optparse.rb, line 1268
      def order!(argv = default_argv, &nonopt)
        opt, arg, sw, val, rest = nil
        nonopt ||= proc {|arg| throw :terminate, arg}
        argv.unshift(arg) if arg = catch(:terminate) {
          while arg = argv.shift
            case arg
            # long option
            when /\A--([^=]*)(?:=(.*))?/
              opt, rest = $1, $2
              begin
                sw, = complete(:long, opt, true)
              rescue ParseError
                raise $!.set_option(arg, true)
              end
              begin
                opt, sw, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
                sw.call(val) if sw
              rescue ParseError
                raise $!.set_option(arg, rest)
              end

            # short option
            when /\A-(.)((=).*|.+)?/
              opt, has_arg, eq, val, rest = $1, $3, $3, $2, $2
              begin
                unless sw = search(:short, opt)
                  begin
                    sw, = complete(:short, opt)
                    # short option matched.
                    val = arg.sub(/\A-/, '')
                    has_arg = true
                  rescue InvalidOption
                    # if no short options match, try completion with long
                    # options.
                    sw, = complete(:long, opt)
                    eq ||= !rest
                  end
                end
              rescue ParseError
                raise $!.set_option(arg, true)
              end
              begin
                opt, sw, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
                raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
                argv.unshift(opt) if opt and (opt = opt.sub(/\A-*/, '-')) != '-'
                sw.call(val) if sw
              rescue ParseError
                raise $!.set_option(arg, arg.length > 2)
              end

            # non-option argument
            else
              nonopt.call(arg)
            end
          end

          nil
        }

        argv
      end

parse(*argv)

[Source]

    # File optparse.rb, line 1369
      def parse(*argv)
        argv = argv[0].dup if argv.size == 1 and Array === argv[0]
        parse!(argv)
      end

parse!(argv = default_argv)

[Source]

    # File optparse.rb, line 1374
      def parse!(argv = default_argv)
        if ENV.include?('POSIXLY_CORRECT')
          order!(argv)
        else
          permute!(argv)
        end
      end

permute(*argv)

[Source]

    # File optparse.rb, line 1343
      def permute(*argv)
        argv = argv[0].dup if argv.size == 1 and Array === argv[0]
        permute!(argv)
      end

permute!(argv = default_argv)

[Source]

    # File optparse.rb, line 1348
      def permute!(argv = default_argv)
        nonopts = []
        arg = nil
        order!(argv) {|arg| nonopts << arg}
        argv[0, 0] = nonopts
        argv
      end

program_name()

[Source]

    # File optparse.rb, line 901
      def program_name
        @program_name || File.basename($0, '.*')
      end

reject(*args, &blk)

[Source]

    # File optparse.rb, line 867
      def reject(*args, &blk) top.reject(*args, &blk) end

release()

[Source]

    # File optparse.rb, line 928
      def release
        @release || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
      end

remove()

[Source]

    # File optparse.rb, line 981
      def remove
        @stack.pop
      end

separator(string)

[Source]

    # File optparse.rb, line 1241
      def separator(string)
        top.append(string, nil, nil)
      end

summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)

[Source]

    # File optparse.rb, line 1001
      def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
        visit(:summarize, {}, {}, width, max, indent, &(blk || proc {|l| to << l + $/}))
        to
      end

terminate(arg = nil)

[Source]

    # File optparse.rb, line 834
      def terminate(arg = nil)
        self.class.terminate(arg)
      end

to_a()

[Source]

    # File optparse.rb, line 1018
      def to_a; summarize(banner.to_a.dup) end

to_s()

Alias for help

top()

[Source]

    # File optparse.rb, line 952
      def top
        @stack[-1]
      end

ver()

[Source]

    # File optparse.rb, line 932
      def ver
        if v = version
          str = "#{program_name} #{[v].join('.')}"
          str << " (#{v})" if v = release
          str
        end
      end

version()

[Source]

    # File optparse.rb, line 924
      def version
        @version || (defined?(::Version) && ::Version)
      end

warn(mesg = $!)

[Source]

    # File optparse.rb, line 940
      def warn(mesg = $!)
        super("#{program_name}: #{mesg}")
      end

[Validate]