Class: CommandLine::OptionParser
- Inherits:
-
Object
- Object
- CommandLine::OptionParser
- Defined in:
- lib/commandline/optionparser/optionparser.rb
Defined Under Namespace
Classes: DuplicateOptionNameError, MissingRequiredOptionArgumentError, MissingRequiredOptionError, OptionParserError, PosixMismatchError, UnknownOptionError, UnknownPropertyError
Constant Summary collapse
- DEFAULT_CONSOLE_WIDTH =
70
- MIN_CONSOLE_WIDTH =
10
- DEFAULT_BODY_INDENT =
4
- OPT_NOT_FOUND_BUT_REQUIRED =
These helper lambdas are here because OptionParser is the object that calls them and hence knows the parameter order.
lambda { |opt| raise(MissingRequiredOptionError, "Missing required parameter '#{opt.names[0]}'.") }
- GET_ARG_ARRAY =
lambda { |opt, user_opt, args| args }
- GET_ARGS =
lambda { |opt, user_opt, args| return true if args.empty? return args[0] if 1 == args.size args }
Instance Attribute Summary collapse
-
#body_indent ⇒ Object
Returns the value of attribute body_indent.
-
#columns ⇒ Object
Returns the value of attribute columns.
-
#posix ⇒ Object
readonly
Returns the value of attribute posix.
-
#tag_paragraph ⇒ Object
Returns the value of attribute tag_paragraph.
-
#unknown_options ⇒ Object
readonly
Returns the value of attribute unknown_options.
-
#unknown_options_action ⇒ Object
readonly
Returns the value of attribute unknown_options_action.
Instance Method Summary collapse
-
#<<(option) ⇒ Object
Add an option.
- #add_names(*options) ⇒ Object
- #get_opt_args(opt, user_option, args) ⇒ Object
- #get_posix_re ⇒ Object
-
#initialize(*opts_and_props) {|_self| ... } ⇒ OptionParser
constructor
A new instance of OptionParser.
-
#parse(argv = ARGV) ⇒ Object
Parse the command line.
-
#parse_argv(argv, &block) ⇒ Object
Seperates options from arguments Does not look for valid options ( or should it? ).
- #parse_posix_argv(argv) ⇒ Object
- #to_s(sep = "\n") ⇒ Object
- #to_str ⇒ Object
- #validate_parse_options(h) ⇒ Object
Constructor Details
#initialize(*opts_and_props) {|_self| ... } ⇒ OptionParser
Returns a new instance of OptionParser.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/commandline/optionparser/optionparser.rb', line 58 def initialize(*opts_and_props) @posix = false @unknown_options_action = :raise @unknown_options = [] @opt_lookup_by_any_name = {} @command_options = nil # # Formatting defaults # console_width = ENV["COLUMNS"] @columns = if console_width.nil? DEFAULT_CONSOLE_WIDTH elsif console_width < MIN_CONSOLE_WIDTH console_width else console_width - DEFAULT_BODY_INDENT end @body_indent = DEFAULT_BODY_INDENT @tag_paragraph = false @order = :index # | :alpha props = [] keys = {} opts_and_props.flatten! opts_and_props.delete_if { |op| if Symbol === op props << op; true elsif Hash === op keys.update(op); true else false end } props.each { |p| case p when :posix then @posix = true else raise(UnknownPropertyError, "Unknown property '#{p.inspect}'.") end } keys.each { |k,v| case k when :unknown_options_action if [:collect, :ignore, :raise].include?(v) @unknown_options_action = v else raise(UnknownPropertyError, "Unknown value '#{v}' for "+ ":unknown_options property.") end when :command_options @command_options = v @commands = v.keys else raise(UnknownPropertyError, "Unknown property '#{k.inspect}'.") end } # :unknown_options => :collect # :unknown_options => :ignore # :unknown_options => :raise opts = opts_and_props @options = [] opts.each { |opt| # If user wants to parse posix, then ensure all options are posix raise(PosixMismatchError, "Posix types do not match. #{opt.inspect}") if @posix && !opt.posix @options << opt } #p "options-"*5 #p @options add_names(@options) yield self if block_given? end |
Instance Attribute Details
#body_indent ⇒ Object
Returns the value of attribute body_indent.
21 22 23 |
# File 'lib/commandline/optionparser/optionparser.rb', line 21 def body_indent @body_indent end |
#columns ⇒ Object
Returns the value of attribute columns.
21 22 23 |
# File 'lib/commandline/optionparser/optionparser.rb', line 21 def columns @columns end |
#posix ⇒ Object (readonly)
Returns the value of attribute posix.
19 20 21 |
# File 'lib/commandline/optionparser/optionparser.rb', line 19 def posix @posix end |
#tag_paragraph ⇒ Object
Returns the value of attribute tag_paragraph.
21 22 23 |
# File 'lib/commandline/optionparser/optionparser.rb', line 21 def tag_paragraph @tag_paragraph end |
#unknown_options ⇒ Object (readonly)
Returns the value of attribute unknown_options.
19 20 21 |
# File 'lib/commandline/optionparser/optionparser.rb', line 19 def @unknown_options end |
#unknown_options_action ⇒ Object (readonly)
Returns the value of attribute unknown_options_action.
19 20 21 |
# File 'lib/commandline/optionparser/optionparser.rb', line 19 def @unknown_options_action end |
Instance Method Details
#<<(option) ⇒ Object
Add an option
161 162 163 164 165 |
# File 'lib/commandline/optionparser/optionparser.rb', line 161 def <<(option) @options << option add_names(option) self end |
#add_names(*options) ⇒ Object
167 168 169 170 171 172 173 174 175 176 |
# File 'lib/commandline/optionparser/optionparser.rb', line 167 def add_names(*) .flatten.each { |option| option.names.each { |name| raise(DuplicateOptionNameError, "Duplicate option name '#{name}'.") if @opt_lookup_by_any_name.has_key?(name) @opt_lookup_by_any_name[name] = option } } end |
#get_opt_args(opt, user_option, args) ⇒ Object
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/commandline/optionparser/optionparser.rb', line 270 def get_opt_args(opt, user_option, args) min, max = *opt.arg_arity size = args.size if (min == max && max > 0 && size < max) || (size < min) raise(MissingRequiredOptionArgumentError, "Insufficient arguments #{args.inspect}for option '#{user_option}' "+ "with :arg_arity #{opt.arg_arity.inspect}") end if 0 == min && 0 == max [] else max = size if -1 == max args.slice!(0..[min, [max, size].min].max - 1) end end |
#get_posix_re ⇒ Object
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/commandline/optionparser/optionparser.rb', line 288 def get_posix_re flags = [] nflags = [] @options.each { |o| if [0,0] == o.arg_arity flags << o.names[0][1..1] else nflags << o.names[0][1..1] end } flags = flags.join flags = flags.empty? ? "" : "[#{flags}\]+" nflags = nflags.join nflags = nflags.empty? ? "" : "[#{nflags}\]" Regexp.new("^-(#{flags})(#{nflags})(.*)\$") end |
#parse(argv = ARGV) ⇒ Object
Parse the command line
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/commandline/optionparser/optionparser.rb', line 196 def parse(argv=ARGV) argv = [argv] unless Array === argv # # Holds the results of each option. The key used is # the first in the :names Array. # opts = Hash.new( :not_found ) # # A command is the first non-option free argument on the command line. # This is a user selection and is the first argument in args. # cmd = args.shift # Example: # cvs -v cmd --cmd-option arg # cmd = nil = {} # # #parse_argv yields an array containing the option and its arguments. # [opts, array_args] # How do we collect all the arguments when OptionParser deal with an # empty option list # parse_argv(argv) { |optarg| user_option = optarg[0] args = optarg[1] m = nil if @opt_lookup_by_any_name.has_key?(user_option) || 1 == (m = @opt_lookup_by_any_name.keys.grep(/^#{user_option}/)).size user_option = m[0] if m opt = @opt_lookup_by_any_name[user_option] opt_key = opt.names[0] opts[opt_key] = if Proc === opt.opt_found # Take the arguments depending upon arity opt_args = get_opt_args(opt, user_option, args) opt.opt_found.call(opt, user_option, opt_args) else opt.opt_found end # Collect any remaining args @args += args elsif :collect == @unknown_options_action @unknown_options << user_option elsif :ignore == @unknown_options_action else raise(UnknownOptionError, "Unknown option '#{user_option}' in "+ "#{@opt_lookup_by_any_name.inspect}.") end } # # Call :not_found for all the options not on the command line. # @options.each { |opt| name = opt.names[0] if :not_found == opts[name] opts[name] = if Proc === opt.opt_not_found opt.opt_not_found.call(opt) else opt.opt_not_found end end } OptionData.new(argv, opts, @unknown_options, @args, @not_parsed, cmd) end |
#parse_argv(argv, &block) ⇒ Object
Seperates options from arguments Does not look for valid options ( or should it? )
%w(-fred file1 file2) => ["-fred", ["file1", "file2"]]
%w(--fred -t -h xyz) => ["--fred", []] ["-t", []] ["-h", ["xyz"]]
%w(-f=file) => ["-f", ["file"]]
%w(--file=fred) => ["--file", ["fred"]]
%w(-file=fred) => ["-file", ["fred"]]
['-file="fred1 fred2"'] => ["-file", ["fred1", "fred2"]]
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
# File 'lib/commandline/optionparser/optionparser.rb', line 386 def parse_argv(argv, &block) return parse_posix_argv(argv, &block) if @posix @not_parsed = [] tagged = [] argv.each_with_index { |e,i| if "--" == e @not_parsed = argv[(i+1)..(argv.size+1)] break elsif "-" == e tagged << [:arg, e] elsif ?- == e[0] m = Option::GENERAL_OPT_EQ_ARG_RE.match(e) if m.nil? tagged << [:opt, e] else tagged << [:opt, m[1]] tagged << [:arg, m[2]] end else tagged << [:arg, e] end } # # The tagged array has the form: # [ # [:opt, "-a"], [:arg, "filea"], # [:opt, "-b"], [:arg, "fileb"], # #[:not_parsed, ["-z", "-y", "file", "file2", "-a", "-b"]] # ] # # Now, combine any adjacent args such that # [[:arg, "arg1"], [:arg, "arg2"]] # becomes # [[:args, ["arg1", "arg2"]]] # and the final result should be # [ "--file", ["arg1", "arg2"]] # parsed = [] @args = [] tagged.each { |e| if :opt == e[0] parsed << [e[1], []] elsif :arg == e[0] if Array === parsed[-1] parsed[-1][-1] += [e[1]] else @args << e[1] end else raise "How did we get here?" end } parsed.each { |e| block.call(e) } end |
#parse_posix_argv(argv) ⇒ Object
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/commandline/optionparser/optionparser.rb', line 306 def parse_posix_argv(argv) re = @posix ? get_posix_re : Option::GENERAL_OPT_EQ_ARG_RE p re if $DEBUG tagged = [] # # A Posix command line must have all the options precede # non option arguments. For example # :names => -h -e -l -p -s # where -p can take an argument # Command line can read: # -helps => -h -e -l -p s # -p fred non-opt-arg # -p fred non-opt-arg -h # not ok # -he -popt-arg1 -popt-arg2 non-opt-arg # -p=fred # this is not legal? # -pfred === -p fred # #"-helps" "-pfred" "-p" "fred" #-h -e -l -p [s] -p [fred] -p [fred] #[-h, []], [-e []], [-l, []], [-p, [s]], -p argv.each { |e| m = re.match(e) if m.nil? tagged << [:arg, e] else raise "houston, we have a problem" if m.nil? unless m[1].empty? m[1].split(//).each { |e| tagged << [:opt, "-#{e}"] } end unless m[2].empty? tagged << [:opt, "-#{m[2]}"] tagged << [:arg, m[3]] unless m[3].empty? end end } if $DEBUG print "Tagged:" p tagged end # # Now, combine any adjacent args such that # [[:arg, "arg1"], [:arg, "arg2"]] # becomes # [[:args, ["arg1", "arg2"]]] # and the final result should be # [ "--file", ["arg1", "arg2"]] # parsed = [] @args = [] tagged.each { |e| if :opt == e[0] parsed << [e[1], []] else if Array === parsed[-1] parsed[-1][-1] += [e[1]] else @args << e[1] end end } parsed.each { |e| yield e } end |
#to_s(sep = "\n") ⇒ Object
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 |
# File 'lib/commandline/optionparser/optionparser.rb', line 449 def to_s(sep="\n") require 'commandline/text/format' @f = Text::Format.new @f.columns = @columns @f.first_indent = 4 @f.body_indent = 8 @f.tag_paragraph = false header = ["OPTIONS\n"] s = [] @options.each { |opt| opt_str = [] if block_given? result = yield(opt.names, opt.opt_description, opt.arg_description) if result.kind_of?(String) opt_str << result unless result.empty? elsif result.nil? opt_str << format_option(opt.names, opt.opt_description, opt.arg_description) elsif result.kind_of?(Array) && 3 == result.size opt_str << format_option(*result) else raise "Invalid return value #{result.inspect} from yield block "+ "attached to #to_s." end else opt_str << format_option(opt.names, opt.opt_description, opt.arg_description) end s << opt_str.join unless opt_str.empty? } #s.collect! { |i| i.kind_of?(Array) && /\n+/ =~ i[0] ? i.join : f.paragraphs(i) } [header, s].flatten.join(sep) end |
#to_str ⇒ Object
445 446 447 |
# File 'lib/commandline/optionparser/optionparser.rb', line 445 def to_str to_s end |
#validate_parse_options(h) ⇒ Object
178 179 180 181 182 183 184 185 186 187 |
# File 'lib/commandline/optionparser/optionparser.rb', line 178 def (h) h[:names].each { |name| check_option_name(name) } #if @posix # all are single-dash:single-char OR double-dash:multi-char #else if unix compliant # single-dash only #else any - does not support combination - try to on single/single #end end |