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.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#posix ⇒ Object
readonly
Returns the value of attribute posix.
-
#tag_paragraph ⇒ Object
Returns the value of attribute tag_paragraph.
-
#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
- #add_option(*h) ⇒ 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.
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 |
# File 'lib/commandline/optionparser/optionparser.rb', line 59 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 } add_names(@options) yield self if block_given? end |
Instance Attribute Details
#body_indent ⇒ Object
Returns the value of attribute body_indent.
22 23 24 |
# File 'lib/commandline/optionparser/optionparser.rb', line 22 def body_indent @body_indent end |
#columns ⇒ Object
Returns the value of attribute columns.
22 23 24 |
# File 'lib/commandline/optionparser/optionparser.rb', line 22 def columns @columns end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
19 20 21 |
# File 'lib/commandline/optionparser/optionparser.rb', line 19 def @options 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.
22 23 24 |
# File 'lib/commandline/optionparser/optionparser.rb', line 22 def tag_paragraph @tag_paragraph 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
160 161 162 163 164 |
# File 'lib/commandline/optionparser/optionparser.rb', line 160 def <<(option) @options << option add_names(option) self end |
#add_names(*options) ⇒ Object
172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/commandline/optionparser/optionparser.rb', line 172 def add_names(*) .flatten.each { |option| raise "Wrong data type '#{option.name}." unless Option === 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 |
#add_option(*h) ⇒ Object
166 167 168 169 170 |
# File 'lib/commandline/optionparser/optionparser.rb', line 166 def add_option(*h) opt = Option.new(*h) @options << opt add_names(opt) end |
#get_opt_args(opt, user_option, _args) ⇒ Object
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/commandline/optionparser/optionparser.rb', line 275 def get_opt_args(opt, user_option, _args) min, max = *opt.arity size = _args.size if (min == max && max > 0 && size < max) || (size < min) raise(MissingRequiredOptionArgumentError, "Insufficient arguments #{_args.inspect}for option '#{user_option}' "+ "with :arity #{opt.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
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/commandline/optionparser/optionparser.rb', line 293 def get_posix_re flags = [] nflags = [] @options.each { |o| if [0,0] == o.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
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 269 270 271 272 273 |
# File 'lib/commandline/optionparser/optionparser.rb', line 202 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] opt_args = get_opt_args(opt, user_option, _args) opts[opt_key] = if Proc === opt.opt_found # Take the arguments depending upon arity 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}'"+ "#{$DEBUG ? ' in ' + @opt_lookup_by_any_name.keys.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"]]
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 444 445 446 447 448 |
# File 'lib/commandline/optionparser/optionparser.rb', line 391 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
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 374 375 376 377 378 |
# File 'lib/commandline/optionparser/optionparser.rb', line 311 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
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 481 482 483 484 485 486 487 |
# File 'lib/commandline/optionparser/optionparser.rb', line 454 def to_s(sep="\n") return "" if @options.empty? require '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
450 451 452 |
# File 'lib/commandline/optionparser/optionparser.rb', line 450 def to_str to_s end |
#validate_parse_options(h) ⇒ Object
184 185 186 187 188 189 190 191 192 193 |
# File 'lib/commandline/optionparser/optionparser.rb', line 184 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 |