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] | |
program_name | [W] | |
release | [W] | |
summary_indent | [RW] | |
summary_width | [RW] | |
version | [W] |
# File optparse/version.rb, line 47 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 781 def self.inc(arg, default = nil) case arg when Integer arg.nonzero? when nil default.to_i + 1 end end
# File optparse.rb, line 806 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 yield self if block_given? end
# File optparse/version.rb, line 57 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 show = proc do |klass, version| version = version.join(".") if Array === version str = "#{progname}" str << ": #{klass}" unless klass == Object str << " version #{version}" case when klass.const_defined?(:Release) str << " (#{klass.const_get(:Release)})" when klass.const_defined?(:RELEASE) str << " (#{klass.const_get(:Release)})" end puts str 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, version) end end else pkg.each do |pkg| /\A[A-Z]\w*((::|\/)[A-Z]\w*)*\z/ni =~ pkg or next begin pkg = eval(pkg) v = case when pkg.const_defined?(:Version) pkg.const_get(:Version) when pkg.const_defined?(:VERSION) pkg.const_get(:VERSION) else "unknown" end show.call(pkg, v) rescue NameError puts "#{progname}: #$!" end end end exit end
# File optparse.rb, line 770 def self.with(*args, &block) opts = new(*args) opts.instance_eval(&block) opts end
# File optparse.rb, line 1196 def define(*opts, &block) top.append(*(sw = make_switch(*opts, &block))) sw[0] end
# File optparse.rb, line 1206 def define_head(*opts, &block) top.prepend(*(sw = make_switch(*opts, &block))) sw[0] end
# File optparse.rb, line 1216 def define_tail(*opts, &block) base.append(*(sw = make_switch(*opts, &block))) sw[0] end
# File optparse.rb, line 1464 def environment(env = File.basename($0, '.*')) env = ENV[env] || ENV[env.upcase] or return parse(*Shellwords.shellwords(env)) end
# File optparse.rb, line 1442 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 1068 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 953 def new @stack.push(List.new) if block_given? yield self else self end end
# File optparse.rb, line 1248 def order(*argv, &block) argv = argv[0].dup if argv.size == 1 and Array === argv[0] order!(argv, &block) end
# File optparse.rb, line 1253 def order!(argv = 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) 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 1354 def parse(*argv) argv = argv[0].dup if argv.size == 1 and Array === argv[0] parse!(argv) end
# File optparse.rb, line 1359 def parse!(argv = ARGV) if ENV.include?('POSIXLY_CORRECT') order!(argv) else permute!(argv) end end
# File optparse.rb, line 1328 def permute(*argv) argv = argv[0].dup if argv.size == 1 and Array === argv[0] permute!(argv) end
# File optparse.rb, line 1333 def permute!(argv = ARGV) nonopts = [] arg = nil order!(argv) {|arg| nonopts << arg} argv[0, 0] = nonopts argv end
# File optparse.rb, line 913 def release @release || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE) end
# File optparse.rb, line 986 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 917 def ver if v = version str = "#{program_name} #{[v].join('.')}" str << " (#{v})" if v = release str end end
# File optparse.rb, line 909 def version @version || (defined?(::Version) && ::Version) || (defined?(::VERSION) && ::VERSION) end