Class | OptionParser |
In: |
optparse.rb
|
Parent: | Object |
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.
All of these features are demonstrated in the example below.
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.
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.
DecimalInteger | = | /\A[-+]?#{decimal}/io |
OctalInteger | = | /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))/io |
DecimalNumeric | = | floatpat # decimal integer is allowed as float also. |
banner= | -> | set_banner |
for experimental cascading :-) | ||
program_name= | -> | set_program_name |
summary_width= | -> | set_summary_width |
summary_indent= | -> | set_summary_indent |
banner | [W] | |
default_argv | [RW] | |
program_name | [W] | |
release | [W] | |
summary_indent | [RW] | |
summary_width | [RW] | |
version | [W] |
# 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
# 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
# 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
# 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
# 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
# File optparse.rb, line 771 def self.with(*args, &block) opts = new(*args) opts.instance_eval(&block) opts end
:nodoc:
# File optparse.rb, line 819 def add_officious list = base() Officious.each_pair do |opt, block| list.long[opt] ||= block.call(self) end end
# File optparse.rb, line 1211 def define(*opts, &block) top.append(*(sw = make_switch(*opts, &block))) sw[0] end
# File optparse.rb, line 1221 def define_head(*opts, &block) top.prepend(*(sw = make_switch(*opts, &block))) sw[0] end
# File optparse.rb, line 1231 def define_tail(*opts, &block) base.append(*(sw = make_switch(*opts, &block))) sw[0] end
# File optparse.rb, line 1481 def environment(env = File.basename($0, '.*')) env = ENV[env] || ENV[env.upcase] or return parse(*Shellwords.shellwords(env)) end
# 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
# 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
# File optparse.rb, line 968 def new @stack.push(List.new) if block_given? yield self else self end end
# 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
# 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
# File optparse.rb, line 1369 def parse(*argv) argv = argv[0].dup if argv.size == 1 and Array === argv[0] parse!(argv) end
# File optparse.rb, line 1374 def parse!(argv = default_argv) if ENV.include?('POSIXLY_CORRECT') order!(argv) else permute!(argv) end end
# File optparse.rb, line 1343 def permute(*argv) argv = argv[0].dup if argv.size == 1 and Array === argv[0] permute!(argv) end
# File optparse.rb, line 1348 def permute!(argv = default_argv) nonopts = [] arg = nil order!(argv) {|arg| nonopts << arg} argv[0, 0] = nonopts argv end
# File optparse.rb, line 928 def release @release || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE) end
# 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
# File optparse.rb, line 932 def ver if v = version str = "#{program_name} #{[v].join('.')}" str << " (#{v})" if v = release str end end