optparse-plus 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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