optparse-plus 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +7 -0
  6. data/CHANGES.md +66 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE.txt +201 -0
  9. data/README.rdoc +173 -0
  10. data/Rakefile +94 -0
  11. data/bin/optparse_plus +130 -0
  12. data/fix.rb +29 -0
  13. data/lib/optparse-plus.rb +1 -0
  14. data/lib/optparse_plus.rb +15 -0
  15. data/lib/optparse_plus/argv_parser.rb +50 -0
  16. data/lib/optparse_plus/cli.rb +116 -0
  17. data/lib/optparse_plus/cli_logger.rb +133 -0
  18. data/lib/optparse_plus/cli_logging.rb +138 -0
  19. data/lib/optparse_plus/cucumber.rb +119 -0
  20. data/lib/optparse_plus/error.rb +32 -0
  21. data/lib/optparse_plus/execution_strategy/base.rb +34 -0
  22. data/lib/optparse_plus/execution_strategy/jvm.rb +37 -0
  23. data/lib/optparse_plus/execution_strategy/mri.rb +16 -0
  24. data/lib/optparse_plus/execution_strategy/open_3.rb +16 -0
  25. data/lib/optparse_plus/execution_strategy/open_4.rb +22 -0
  26. data/lib/optparse_plus/execution_strategy/rbx_open_4.rb +12 -0
  27. data/lib/optparse_plus/exit_now.rb +40 -0
  28. data/lib/optparse_plus/main.rb +603 -0
  29. data/lib/optparse_plus/process_status.rb +45 -0
  30. data/lib/optparse_plus/sh.rb +223 -0
  31. data/lib/optparse_plus/test/base_integration_test.rb +31 -0
  32. data/lib/optparse_plus/test/integration_test_assertions.rb +65 -0
  33. data/lib/optparse_plus/version.rb +3 -0
  34. data/optparse_plus.gemspec +28 -0
  35. data/templates/full/.gitignore.erb +4 -0
  36. data/templates/full/README.rdoc.erb +24 -0
  37. data/templates/full/Rakefile.erb +71 -0
  38. data/templates/full/_license_head.txt.erb +2 -0
  39. data/templates/full/apache_LICENSE.txt.erb +203 -0
  40. data/templates/full/bin/executable.erb +45 -0
  41. data/templates/full/custom_LICENSE.txt.erb +0 -0
  42. data/templates/full/gplv2_LICENSE.txt.erb +14 -0
  43. data/templates/full/gplv3_LICENSE.txt.erb +14 -0
  44. data/templates/full/mit_LICENSE.txt.erb +7 -0
  45. data/templates/rspec/spec/something_spec.rb.erb +5 -0
  46. data/templates/test_unit/test/integration/test_cli.rb.erb +11 -0
  47. data/templates/test_unit/test/unit/test_something.rb.erb +7 -0
  48. data/test/integration/base_integration_test.rb +60 -0
  49. data/test/integration/test_bootstrap.rb +150 -0
  50. data/test/integration/test_cli.rb +21 -0
  51. data/test/integration/test_license.rb +56 -0
  52. data/test/integration/test_readme.rb +53 -0
  53. data/test/integration/test_rspec.rb +28 -0
  54. data/test/integration/test_version.rb +21 -0
  55. data/test/unit/base_test.rb +19 -0
  56. data/test/unit/command_for_tests.sh +7 -0
  57. data/test/unit/execution_strategy/test_base.rb +24 -0
  58. data/test/unit/execution_strategy/test_jvm.rb +77 -0
  59. data/test/unit/execution_strategy/test_mri.rb +32 -0
  60. data/test/unit/execution_strategy/test_open_3.rb +70 -0
  61. data/test/unit/execution_strategy/test_open_4.rb +86 -0
  62. data/test/unit/execution_strategy/test_rbx_open_4.rb +25 -0
  63. data/test/unit/test/test_integration_test_assertions.rb +211 -0
  64. data/test/unit/test_cli_logger.rb +219 -0
  65. data/test/unit/test_cli_logging.rb +243 -0
  66. data/test/unit/test_exit_now.rb +37 -0
  67. data/test/unit/test_main.rb +840 -0
  68. data/test/unit/test_sh.rb +404 -0
  69. metadata +260 -0
@@ -0,0 +1,603 @@
1
+ require 'optparse'
2
+ require 'yaml'
3
+
4
+ begin
5
+ Module.const_get('BasicObject')
6
+ # We are 1.9.x
7
+ rescue NameError
8
+ BasicObject = Object
9
+ end
10
+
11
+ module OptparsePlus
12
+ # Include this module to gain access to the "canonical command-line app structure"
13
+ # DSL. This is a *very* lightweight layer on top of what you might
14
+ # normally write that gives you just a bit of help to keep your code structured
15
+ # in a sensible way. You can use as much or as little as you want, though
16
+ # you must at least use #main to get any benefits.
17
+ #
18
+ # Further, you must provide access to a logger via a method named
19
+ # #logger. If you include OptparsePlus::CLILogging, this will be done for you
20
+ #
21
+ # You also get a more expedient interface to OptionParser as well
22
+ # as checking for required arguments to your app. For example, if
23
+ # we want our app to accept a negatable switch named "switch", a flag
24
+ # named "flag", and two arguments "needed" (which is required)
25
+ # and "maybe" which is optional, we can do the following:
26
+ #
27
+ # #!/usr/bin/env ruby
28
+ #
29
+ # require 'optparse_plus'
30
+ #
31
+ # class App
32
+ # include OptparsePlus::Main
33
+ # include OptparsePlus::CLILogging
34
+ #
35
+ # main do |needed, maybe|
36
+ # options[:switch] => true or false, based on command line
37
+ # options[:flag] => value of flag passed on command line
38
+ # end
39
+ #
40
+ # # Proxy to an OptionParser instance's on method
41
+ # on("--[no]-switch")
42
+ # on("--flag VALUE")
43
+ #
44
+ # arg :needed
45
+ # arg :maybe, :optional
46
+ #
47
+ # defaults_from_env_var SOME_VAR
48
+ # defaults_from_config_file '.my_app.rc'
49
+ #
50
+ # go!
51
+ # end
52
+ #
53
+ # Our app then acts as follows:
54
+ #
55
+ # $ our_app
56
+ # # => parse error: 'needed' is required
57
+ # $ our_app foo
58
+ # # => succeeds; "maybe" in main is nil
59
+ # $ our_app --flag foo
60
+ # # => options[:flag] has the value "foo"
61
+ # $ SOME_VAR='--flag foo' our_app
62
+ # # => options[:flag] has the value "foo"
63
+ # $ SOME_VAR='--flag foo' our_app --flag bar
64
+ # # => options[:flag] has the value "bar"
65
+ #
66
+ # Note that we've done all of this inside a class that we called +App+. This isn't strictly
67
+ # necessary, and you can just +include+ OptparsePlus::Main and OptparsePlus::CLILogging at the root
68
+ # of your +bin+ file if you like. This is somewhat unsafe, because +self+ inside the +bin+
69
+ # file is Object, and any methods you create (or cause to be created via +include+) will be
70
+ # present on *every* object. This can cause odd problems, so it's recommended that you
71
+ # *not* do this.
72
+ #
73
+ module Main
74
+ include OptparsePlus::ExitNow
75
+ include OptparsePlus::ARGVParser
76
+
77
+ def self.included(k)
78
+ k.extend(self)
79
+ end
80
+
81
+ # Declare the main method for your app.
82
+ # This allows you to specify the general logic of your
83
+ # app at the top of your bin file, but can rely on any methods
84
+ # or other code that you define later.
85
+ #
86
+ # For example, suppose you want to process a set of files, but
87
+ # wish to determine that list from another method to keep your
88
+ # code clean.
89
+ #
90
+ # #!/usr/bin/env ruby -w
91
+ #
92
+ # require 'optparse_plus'
93
+ #
94
+ # include OptparsePlus::Main
95
+ #
96
+ # main do
97
+ # files_to_process.each do |file|
98
+ # # process file
99
+ # end
100
+ # end
101
+ #
102
+ # def files_to_process
103
+ # # return list of files
104
+ # end
105
+ #
106
+ # go!
107
+ #
108
+ # The block can accept any parameters, and unparsed arguments
109
+ # from the command line will be passed.
110
+ #
111
+ # *Note*: #go! will modify +ARGV+ so any unparsed arguments that you do *not* declare as arguments
112
+ # to #main will essentially be unavailable. I consider this a bug, and it should be changed/fixed in
113
+ # a future version.
114
+ #
115
+ # To run this method, call #go!
116
+ def main(&block)
117
+ @main_block = block
118
+ end
119
+
120
+ # Configure the auto-handling of StandardError exceptions caught
121
+ # from calling go!.
122
+ #
123
+ # leak:: if true, go! will *not* catch StandardError exceptions, but instead
124
+ # allow them to bubble up. If false, they will be caught and handled as normal.
125
+ # This does *not* affect OptparsePlus::Error exceptions; those will NOT leak through.
126
+ def leak_exceptions(leak)
127
+ @leak_exceptions = leak
128
+ end
129
+
130
+ # Set the name of the environment variable where users can place default
131
+ # options for your app. Omit this to disable the feature.
132
+ def defaults_from_env_var(env_var)
133
+ @env_var = env_var
134
+ end
135
+
136
+ # Set the path to the file where defaults can be configured.
137
+ #
138
+ # The format of this file can be either a simple string of options, like what goes
139
+ # in the environment variable (see #defaults_from_env_var), or YAML, in which case
140
+ # it should be a hash where keys are the option names, and values their defaults.
141
+ #
142
+ # Relative paths will be expanded relative to the user's home directory.
143
+ #
144
+ # filename:: path to the file. If relative, will look in user's HOME directory.
145
+ # If absolute, this is the absolute path to where the file should be.
146
+ def defaults_from_config_file(filename,options={})
147
+ @rc_file = File.expand_path(filename, ENV['HOME'])
148
+ end
149
+
150
+ # Start your command-line app, exiting appropriately when
151
+ # complete.
152
+ #
153
+ # This *will* exit your program when it completes. If your
154
+ # #main block evaluates to an integer, that value will be sent
155
+ # to Kernel#exit, otherwise, this will exit with 0
156
+ #
157
+ # If the command-line options couldn't be parsed, this
158
+ # will exit with 64 and whatever message OptionParser provided.
159
+ #
160
+ # If a required argument (see #arg) is not found, this exits with
161
+ # 64 and a message about that missing argument.
162
+ def go!
163
+ setup_defaults
164
+ opts.post_setup
165
+ opts.parse!
166
+ opts.check_args!
167
+ result = call_main
168
+ if result.kind_of? Integer
169
+ exit result
170
+ else
171
+ exit 0
172
+ end
173
+ rescue OptionParser::ParseError => ex
174
+ logger.error ex.message
175
+ puts
176
+ puts opts.help
177
+ exit 64 # Linux standard for bad command line
178
+ end
179
+
180
+ # Returns an OptionParser that you can use
181
+ # to declare your command-line interface. Generally, you
182
+ # won't use this and will use #on directly, but this allows
183
+ # you to have complete control of option parsing.
184
+ #
185
+ # The object returned has
186
+ # an additional feature that implements typical use of OptionParser.
187
+ #
188
+ # opts.on("--flag VALUE")
189
+ #
190
+ # Does this under the covers:
191
+ #
192
+ # opts.on("--flag VALUE") do |value|
193
+ # options[:flag] = value
194
+ # end
195
+ #
196
+ # Since, most of the time, this is all you want to do,
197
+ # this makes it more expedient to do so. The key that is
198
+ # is set in #options will be a symbol <i>and string</i> of the option name, without
199
+ # the leading dashes. Note that if you use multiple option names, a key
200
+ # will be generated for each. Further, if you use the negatable form,
201
+ # only the positive key will be set, e.g. for <tt>--[no-]verbose</tt>,
202
+ # only <tt>:verbose</tt> will be set (to true or false).
203
+ #
204
+ # As an example, this declaration:
205
+ #
206
+ # opts.on("-f VALUE", "--flag")
207
+ #
208
+ # And this command-line invocation:
209
+ #
210
+ # $ my_app -f foo
211
+ #
212
+ # Will result in all of these forms returning the String "foo":
213
+ # * <tt>options['f']</tt>
214
+ # * <tt>options[:f]</tt>
215
+ # * <tt>options['flag']</tt>
216
+ # * <tt>options[:flag]</tt>
217
+ #
218
+ # Further, any one of those keys can be used to determine the default value for the option.
219
+ def opts
220
+ @option_parser ||= OptionParserProxy.new(OptionParser.new,options)
221
+ end
222
+
223
+ # Calls the +on+ method of #opts with the given arguments (see RDoc for #opts for the additional
224
+ # help provided).
225
+ def on(*args,&block)
226
+ opts.on(*args,&block)
227
+ end
228
+
229
+ # Sets the name of an arguments your app accepts. Note
230
+ # that no sanity checking is done on the configuration
231
+ # of your arguments you create via multiple calls to this method.
232
+ # Namely, the last argument should be the only one that is
233
+ # a :many or a :any, but the system here won't sanity check that.
234
+ #
235
+ # +arg_name+:: name of the argument to appear in documentation
236
+ # This will be converted into a String and used to create
237
+ # the banner (unless you have overridden the banner)
238
+ # +options+:: list (not Hash) of options:
239
+ # <tt>:required</tt>:: this arg is required (this is the default)
240
+ # <tt>:optional</tt>:: this arg is optional
241
+ # <tt>:one</tt>:: only one of this arg should be supplied (default)
242
+ # <tt>:many</tt>:: many of this arg may be supplied, but at least one is required
243
+ # <tt>:any</tt>:: any number, include zero, may be supplied
244
+ # A string:: if present, this will be documentation for the argument and appear in the help
245
+ def arg(arg_name,*options)
246
+ opts.arg(arg_name,*options)
247
+ end
248
+
249
+ # Set the description of your app for inclusion in the help output.
250
+ # +desc+:: a short, one-line description of your app
251
+ def description(desc)
252
+ opts.description(desc)
253
+ end
254
+
255
+ # Returns a Hash that you can use to store or retrieve options
256
+ # parsed from the command line. When you put values in here, if you do so
257
+ # *before* you've declared your command-line interface via #on, the value
258
+ # will be used in the docstring to indicate it is the default.
259
+ # You can use either a String or a Symbol and, after #go! is called and
260
+ # the command-line is parsed, the values will be available as both
261
+ # a String and a Symbol.
262
+ #
263
+ # Example
264
+ #
265
+ # main do
266
+ # puts options[:foo] # put the value of --foo that the user provided
267
+ # end
268
+ #
269
+ # options[:foo] = "bar" # set "bar" as the default value for --foo, which
270
+ # # will cause us to include "(default: bar)" in the
271
+ # # docstring
272
+ #
273
+ # on("--foo FOO","Sets the foo")
274
+ # go!
275
+ #
276
+ def options
277
+ @options ||= {}
278
+ end
279
+
280
+ # Set the version of your app so it appears in the
281
+ # banner. This also adds --version as an option to your app which,
282
+ # when used, will act just like --help (see version_options to control this)
283
+ #
284
+ # version:: the current version of your app. Should almost always be
285
+ # YourApp::VERSION, where the module YourApp should've been generated
286
+ # by the bootstrap script
287
+ # version_options:: controls how the version option behaves. If this is a string,
288
+ # then the string will be used as documentation for the --version flag.
289
+ # If a Hash, more configuration is available:
290
+ # custom_docs:: the string to document the --version flag if you don't like the default
291
+ # compact:: if true, --version will just show the app name and version - no help
292
+ # format:: if provided, this can give limited control over the format of the compact
293
+ # version string. It should be a printf-style string and will be given
294
+ # two options: the first is the CLI app name, and the second is the version string
295
+ def version(version,version_options={})
296
+ opts.version(version)
297
+ if version_options.kind_of?(String)
298
+ version_options = { :custom_docs => version_options }
299
+ end
300
+ version_options[:custom_docs] ||= "Show help/version info"
301
+ version_options[:format] ||= "%s version %s"
302
+ opts.on("--version",version_options[:custom_docs]) do
303
+ if version_options[:compact]
304
+ puts version_options[:format] % [::File.basename($0),version]
305
+ else
306
+ puts opts.to_s
307
+ end
308
+ exit 0
309
+ end
310
+ end
311
+
312
+ private
313
+
314
+ # Reset internal state - mostly useful for tests
315
+ def reset!
316
+ @options = nil
317
+ @option_parser = nil
318
+ end
319
+
320
+ def setup_defaults
321
+ add_defaults_to_docs
322
+ set_defaults_from_rc_file
323
+ normalize_defaults
324
+ set_defaults_from_env_var
325
+ end
326
+
327
+ def add_defaults_to_docs
328
+ @env_var = nil unless defined? @env_var
329
+ @rc_file = nil unless defined? @rc_file
330
+ if @env_var && @rc_file
331
+ opts.separator ''
332
+ opts.separator 'Default values can be placed in:'
333
+ opts.separator ''
334
+ opts.separator " #{@env_var} environment variable, as a String of options"
335
+ opts.separator " #{@rc_file} with contents either a String of options "
336
+ spaces = (0..@rc_file.length).reduce('') { |a,_| a << ' ' }
337
+ opts.separator " #{spaces}or a YAML-encoded Hash"
338
+ elsif @env_var
339
+ opts.separator ''
340
+ opts.separator "Default values can be placed in the #{@env_var} environment variable"
341
+ elsif @rc_file
342
+ opts.separator ''
343
+ opts.separator "Default values can be placed in #{@rc_file}"
344
+ end
345
+ end
346
+
347
+ def set_defaults_from_env_var
348
+ if @env_var
349
+ parse_string_for_argv(ENV[@env_var]).each do |arg|
350
+ ::ARGV.unshift(arg)
351
+ end
352
+ end
353
+ end
354
+
355
+ def set_defaults_from_rc_file
356
+ if @rc_file && File.exist?(@rc_file)
357
+ File.open(@rc_file) do |file|
358
+ parsed = begin
359
+ YAML::load(file)
360
+ rescue => ex
361
+ logger.error ex.message unless no_message? ex
362
+ nil
363
+ end
364
+ if parsed.kind_of? String
365
+ parse_string_for_argv(parsed).each do |arg|
366
+ ::ARGV.unshift(arg)
367
+ end
368
+ elsif parsed.kind_of? Hash
369
+ parsed.each do |option,value|
370
+ options[option] = value
371
+ end
372
+ else
373
+ raise OptionParser::ParseError,
374
+ "rc file #{@rc_file} is not parseable, should be a string or YAML-encoded Hash"
375
+ end
376
+ end
377
+ end
378
+ end
379
+
380
+
381
+ # Normalized all defaults to both string and symbol forms, so
382
+ # the user can access them via either means just as they would for
383
+ # non-defaulted options
384
+ def normalize_defaults
385
+ new_options = {}
386
+ options.each do |key,value|
387
+ unless value.nil?
388
+ new_options[key.to_s] = value
389
+ new_options[key.to_sym] = value
390
+ end
391
+ end
392
+ options.merge!(new_options)
393
+ end
394
+
395
+ # Handle calling main and trapping any exceptions thrown
396
+ def call_main
397
+ @leak_exceptions = nil unless defined? @leak_exceptions
398
+ @main_block.call(*ARGV)
399
+ rescue OptparsePlus::Error => ex
400
+ raise ex if ENV['DEBUG']
401
+ logger.error ex.message unless no_message? ex
402
+ ex.exit_code
403
+ rescue OptionParser::ParseError
404
+ raise
405
+ rescue => ex
406
+ raise ex if ENV['DEBUG']
407
+ raise ex if @leak_exceptions
408
+ logger.error ex.message unless no_message? ex
409
+ 70 # Linux sysexit code for internal software error
410
+ end
411
+
412
+ def no_message?(exception)
413
+ exception.message.nil? || exception.message.strip.empty?
414
+ end
415
+ end
416
+
417
+ # <b>OptparsePlus Internal - treat as private</b>
418
+ #
419
+ # A proxy to OptionParser that intercepts #on
420
+ # so that we can allow a simpler interface
421
+ class OptionParserProxy < BasicObject
422
+ # Create the proxy
423
+ #
424
+ # +option_parser+:: An OptionParser instance
425
+ # +options+:: a hash that will store the options
426
+ # set via automatic setting. The caller should
427
+ # retain a reference to this
428
+ def initialize(option_parser,options)
429
+ @option_parser = option_parser
430
+ @options = options
431
+ @user_specified_banner = false
432
+ @accept_options = false
433
+ @args = []
434
+ @arg_options = {}
435
+ @arg_documentation = {}
436
+ @description = nil
437
+ @version = nil
438
+ set_banner
439
+ document_help
440
+ end
441
+
442
+ def check_args!
443
+ ::Hash[@args.zip(::ARGV)].each do |arg_name,arg_value|
444
+ if @arg_options[arg_name].include? :required
445
+ if arg_value.nil?
446
+ message = "'#{arg_name.to_s}' is required"
447
+ message = "at least one " + message if @arg_options[arg_name].include? :many
448
+ raise ::OptionParser::ParseError,message
449
+ end
450
+ end
451
+ end
452
+ end
453
+
454
+ # If invoked as with OptionParser, behaves the exact same way.
455
+ # If invoked without a block, however, the options hash given
456
+ # to the constructor will be used to store
457
+ # the parsed command-line value. See #opts in the Main module
458
+ # for how that works.
459
+ def on(*args,&block)
460
+ @accept_options = true
461
+ args = add_default_value_to_docstring(*args)
462
+ if block
463
+ @option_parser.on(*args,&block)
464
+ else
465
+ opt_names = option_names(*args)
466
+ @option_parser.on(*args) do |value|
467
+ opt_names.each do |name|
468
+ @options[name] = value
469
+ @options[name.to_s] = value
470
+ end
471
+ end
472
+ end
473
+ set_banner
474
+ end
475
+
476
+ # Proxies to underlying OptionParser
477
+ def banner=(new_banner)
478
+ @option_parser.banner=new_banner
479
+ @user_specified_banner = true
480
+ end
481
+
482
+ # Sets the banner to include these arg names
483
+ def arg(arg_name,*options)
484
+ options << :optional if options.include?(:any) && !options.include?(:optional)
485
+ options << :required unless options.include? :optional
486
+ options << :one unless options.include?(:any) || options.include?(:many)
487
+ @args << arg_name
488
+ @arg_options[arg_name] = options
489
+ options.select(&STRINGS_ONLY).each do |doc|
490
+ @arg_documentation[arg_name] = doc + (options.include?(:optional) ? " (optional)" : "")
491
+ end
492
+ set_banner
493
+ end
494
+
495
+ def description(desc)
496
+ @description = desc
497
+ set_banner
498
+ end
499
+
500
+ # Defers all calls save #on to
501
+ # the underlying OptionParser instance
502
+ def method_missing(sym,*args,&block)
503
+ @option_parser.send(sym,*args,&block)
504
+ end
505
+
506
+ # Since we extend Object on 1.8.x, to_s is defined and thus not proxied by method_missing
507
+ def to_s #::nodoc::
508
+ @option_parser.to_s
509
+ end
510
+
511
+ # Sets the version for the banner
512
+ def version(version)
513
+ @version = version
514
+ set_banner
515
+ end
516
+
517
+ # We need some documentation to appear at the end, after all OptionParser setup
518
+ # has occured, but before we actually start. This method serves that purpose
519
+ def post_setup
520
+ unless @arg_documentation.empty?
521
+ @option_parser.separator ''
522
+ @option_parser.separator "Arguments:"
523
+ @option_parser.separator ''
524
+ @args.each do |arg|
525
+ @option_parser.separator " #{arg}"
526
+ @option_parser.separator " #{@arg_documentation[arg]}"
527
+ end
528
+ end
529
+ end
530
+
531
+ private
532
+
533
+ def document_help
534
+ @option_parser.on("-h","--help","Show command line help") do
535
+ puts @option_parser.to_s
536
+ exit 0
537
+ end
538
+ end
539
+
540
+ def add_default_value_to_docstring(*args)
541
+ default_value = nil
542
+ option_names_from(args).each do |option|
543
+ default_value = (@options[option.to_s] || @options[option.to_sym]) if default_value.nil?
544
+ end
545
+ if default_value.nil?
546
+ args
547
+ else
548
+ args + ["(default: #{default_value})"]
549
+ end
550
+ end
551
+
552
+ def option_names_from(args)
553
+ args.select(&STRINGS_ONLY).select { |_|
554
+ _ =~ /^\-/
555
+ }.map { |_|
556
+ _.gsub(/^\-+/,'').gsub(/\s.*$/,'')
557
+ }
558
+ end
559
+
560
+ def set_banner
561
+ unless @user_specified_banner
562
+ new_banner="Usage: #{::File.basename($0)}"
563
+ new_banner += " [options]" if @accept_options
564
+ unless @args.empty?
565
+ new_banner += " "
566
+ new_banner += @args.map { |arg|
567
+ if @arg_options[arg].include? :any
568
+ "[#{arg.to_s}...]"
569
+ elsif @arg_options[arg].include? :optional
570
+ "[#{arg.to_s}]"
571
+ elsif @arg_options[arg].include? :many
572
+ "#{arg.to_s}..."
573
+ else
574
+ arg.to_s
575
+ end
576
+ }.join(' ')
577
+ end
578
+ new_banner += "\n\n#{@description}" if @description
579
+ new_banner += "\n\nv#{@version}" if @version
580
+ new_banner += "\n\nOptions:" if @accept_options
581
+
582
+ @option_parser.banner=new_banner
583
+ end
584
+ end
585
+
586
+ def option_names(*opts_on_args,&block)
587
+ opts_on_args.select(&STRINGS_ONLY).map { |arg|
588
+ if arg =~ /^--\[no-\]([^-\s][^\s]*)/
589
+ $1.to_sym
590
+ elsif arg =~ /^--([^-\s][^\s]*)/
591
+ $1.to_sym
592
+ elsif arg =~ /^-([^-\s][^\s]*)/
593
+ $1.to_sym
594
+ else
595
+ nil
596
+ end
597
+ }.reject(&:nil?)
598
+ end
599
+
600
+ STRINGS_ONLY = lambda { |o| o.kind_of?(::String) }
601
+
602
+ end
603
+ end