I turned this into a gem that you can download and install from rubygems.org:
gem install pickled_optparse
And you can check the updated project source code on github:
http://github.com/PicklePumpers/pickled_optparse
- Old post information -
It was really, really listening to me, so I fixed it and to use super DRY.
To make the required switch, simply add the required character in any of the arrays of such parameters:
opts.on("-f", "--foo [Bar]", String, :required, "Some required option") do |option| @options[:foo] = option end
Then at the end of the OptionParser block, add one of them to print the missing switches and usage instructions:
if opts.missing_switches? puts opts.missing_switches puts opts exit end
And finally, to do all this, you need to add the following file βoptparse_required_switches.rbβ to your project somewhere and require it when you figure out the command line.
I wrote a short article with an example on my blog: http://picklepumpers.com/wordpress/?p=949
And here is a modified OptionParser file with an example of its use:
required_switches_example.rb
optparse_required_switches.rb
# Add required switches to OptionParser class OptionParser # An array of messages describing the missing required switches attr_reader :missing_switches # Convenience method to test if we're missing any required switches def missing_switches? !@missing_switches.nil? end def make_switch(opts, block = nil) 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 n, q, a = nil # Check for required switches required = opts.delete(:required) 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 || Symbol === o)) and o.respond_to?(:match) pattern = notwice(o, pattern, 'pattern') if pattern.respond_to?(:convert) conv = pattern.method(:convert).to_proc else conv = SPLAT_PROC end 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 {|pat, *v| pattern[pat] = v.fetch(0) {pat}} when Module raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4)) 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 if !(short.empty? and long.empty?) s = (style || default_style).new(pattern || default_pattern, conv, sdesc, ldesc, arg, desc, block) elsif !block if style or pattern raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller) end s = desc else short << pattern s = (style || default_style).new(pattern, conv, nil, nil, arg, desc, block) end # Make sure required switches are given if required && !(default_argv.include?("-#{short[0]}") || default_argv.include?("--#{long[0]}")) @missing_switches ||= [] # Should be placed in initialize if incorporated into Ruby proper # This is more clear but ugly and long. #missing = "-#{short[0]}" if !short.empty? #missing = "#{missing} or " if !short.empty? && !long.empty? #missing = "#{missing}--#{long[0]}" if !long.empty? # This is less clear and uglier but shorter. missing = "#{"-#{short[0]}" if !short.empty?}#{" or " if !short.empty? && !long.empty?}#{"--#{long[0]}" if !long.empty?}" @missing_switches << "Missing switch: #{missing}" end return s, short, long, (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style), nolong end end