optimist 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 335830b11859ca8643cd5fe74d6444f147ab2873
4
+ data.tar.gz: 9a483cf4a178eaf83c6093ea177e29cc0cf63c32
5
+ SHA512:
6
+ metadata.gz: 7f18e81587cb3f617d7dfc26183c1d821a058891a638560c60c85202237fb084e57c77ffdc7b5a245a81e378769852b9e51827e8b41329298c7b7f0a3cb2f395
7
+ data.tar.gz: c6f2d0245b46cb58470b4ff6f31ac7df483c8d7f48c1d0da27cdcd3681fef55dced81a2765dc33035b808e7c7ba9519363019bd486f3496a74ebaa11f011f219
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.swp
13
+ *.o
14
+ *.a
15
+ mkmf.log
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm:
4
+ - "2.2"
5
+ - "2.3.4"
6
+ - "2.5.1"
7
+ - jruby-head
8
+ matrix:
9
+ allow_failures:
10
+ - rvm: jruby-head
11
+ fast_finish: true
data/FAQ.txt ADDED
@@ -0,0 +1,92 @@
1
+ Optimist FAQ
2
+ -----------
3
+
4
+ Q: Why should I use Optimist?
5
+ A: Because it will take you fewer lines of code to parse commandline arguments
6
+ than anything else out there.
7
+
8
+ Like this:
9
+ opts = Optimist::options do
10
+ opt :monkey, "Use monkey mode"
11
+ opt :goat, "Use goat mode", :default => true
12
+ opt :num_limbs, "Set number of limbs", :default => 4
13
+ end
14
+
15
+ That's it. 'opts' will be a hash and you can do whatever you want with it.
16
+ You don't have to mix processing code with the declarations. You don't have
17
+ to make a class for every option (what is this, Java?). You don't have to
18
+ write more than 1 line of code per option.
19
+
20
+ Plus, you get a beautiful help screen that detects your terminal width and
21
+ wraps appropriately.
22
+
23
+ Q: What is the philosophy behind Optimist?
24
+ A: Optimist does the parsing and gives you a hash table of options. You then
25
+ write whatever fancy constraint logic you need as regular Ruby code operating
26
+ on that hash table.
27
+
28
+ (Optimist does support limited constraints (see #conflicts and #depends), but
29
+ any non-trivial program will probably need to get fancier.)
30
+
31
+ Then if you need to abort and tell the user to fix their command line at any
32
+ point, you can call #die and Optimist will do that for you in a pretty way.
33
+
34
+ Q: What happens to the other stuff on the commandline?
35
+ A: Anything Optimist doesn't recognize as an option or as an option parameter is
36
+ left in ARGV for you to process.
37
+
38
+ Q: Does Optimist support multiple-value arguments?
39
+ A: Yes. If you set the :type of an option to something plural, like ":ints",
40
+ ":strings", ":doubles", ":floats", ":ios", it will accept multiple arguments
41
+ on the commandline, and the value will be an array of the parameters.
42
+
43
+ Q: Does Optimist support arguments that can be given multiple times?
44
+ A: Yes. If you set :multi to true, then the argument can appear multiple times
45
+ on the commandline, and the value will be an array of the parameters.
46
+
47
+ Q: Does Optimist support subcommands?
48
+ A: Yes: you can direct Optimist to stop processing when it encounters certain
49
+ tokens. Then you can re-call Optimist with the subcommand-specific
50
+ configuration to process the rest of the commandline.
51
+
52
+ See the third example on the webpage.
53
+
54
+ (And if you don't know the subcommands ahead of time, you can call
55
+ #stop_on_unknown, which will cause Optimist to stop when it encounters any
56
+ unknown token. This might be more trouble than its worth if you're also
57
+ passing filenames on the commandline.)
58
+
59
+ Q: Why does Optimist disallow numeric short argument names, like '-1' and '-9'?
60
+ A: Because it's ambiguous whether these are arguments or negative integer or
61
+ floating-point parameters to arguments. E.g., is "-f -3" a negative floating
62
+ point parameter to -f, or two separate arguments?
63
+
64
+ Q: What was the big change in version 2.0?
65
+ A: The big change was boolean parameter (aka flag) handling. In pre-2.0,
66
+ not specifying a flag on the commandline would result in the option being set
67
+ to its default value; specifying it on the commandline would result in the
68
+ option being set to the opposite of its default value. This was weird for
69
+ options with a default of true:
70
+ opt :magic, "Use magic", default: true
71
+ Using --magic with the above configuration would result in a :magic => false
72
+ value in the options hash.
73
+
74
+ In 2.0, we introduce the GNU-style notion of a --no-x parameter. Now,
75
+ specifying --x will always set the option :x to true, regardless of its
76
+ default value, and specifying --no-x will always set the option :x to false,
77
+ regardless of its default value. The default value only comes into play when
78
+ neither form is given on the commandline.
79
+
80
+ E.g.:
81
+ opt :magic, "Use magic", :default => true
82
+
83
+ Using --magic will result in :magic => true, and --no-magic will result in
84
+ :magic => false, and neither will result in :magic => true.
85
+
86
+ There is one exception: if the option itself starts with a "no_", then you'll
87
+ get the opposite behavior:
88
+
89
+ opt :no_magic, "Don't use magic", :default => true
90
+
91
+ Using --magic will result in :no_magic => false, and --no-magic will result in
92
+ :no_magic => true, and neither will result in :no_magic => true.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gemspec
4
+ gemspec
@@ -0,0 +1,168 @@
1
+ == [3.0.0] / 2018-08-24
2
+
3
+ * The gem has been officially renamed to optimist
4
+
5
+ == [2.1.3] / 2018-07-05
6
+
7
+ * Refactor each option type into subclasses of Option. Define a registry for the registration of each option. This makes the code more modular and facilitates extension by allowing additional Option subclasses. (thanks @clxy)
8
+ * Fixed implementation of ignore_invalid_options. (thanks @metcalf)
9
+ * Various warning cleanup for ruby 2.1, 2.3, etc. (thanks @nanobowers)
10
+ * Optimist.die can now accept an error code.
11
+ * fixed default (thanks @nanobowers)
12
+ * Change from ruby license to MIT license in the code.
13
+
14
+ == [2.1.2] / 2015-03-10
15
+ * loosen mime-types requirements (for better ruby 1.8.7 support)
16
+ * use io/console gem instead of curses (for better jruby support)
17
+ * fix parsing bug when chronic gem is not available
18
+ * allow default array to be empty if a type is specified
19
+ * better specified license and better spec coverage
20
+
21
+ == [2.1.1] / 2015-01-03
22
+ * Remove curses as a hard dependency. It is optional. This can leverage the gem if it is present.
23
+ * Fix ruby -w warnings
24
+
25
+ == 2.1.0 / 2015-01-02
26
+ * Integer parser now supports underscore separator.
27
+ * Add Parser#usage and Parser#synopsis commands for creating a standard banner
28
+ message. Using Parser#banner directly will override both of those.
29
+ * Add Parser#ignore_invalid_options to prevent erroring on unknown options.
30
+ * Allow flags to act as switches if they have defaults set and no value is
31
+ passed on the commandline
32
+ * Parser#opt learned to accept a block or a :callback option which it will call
33
+ after parsing the option.
34
+ * Add Optimist::educate which displays the help message and dies.
35
+ * Reformat help message to be more GNUish.
36
+ * Fix command name in help message when script has no extension.
37
+ * Fix handling of newlines inside descriptions
38
+ * Documentation and other fixes.
39
+
40
+ == 2.0 / 2012-08-11
41
+ * Change flag logic: --no-X will always be false, and --X will always be true,
42
+ regardless of default.
43
+ * For flags that default to true, display --no-X instead of --X in the help
44
+ menu. Accept both versions on the commandline.
45
+ * Fix a spurious warning
46
+ * Update Rakefile to 1.9
47
+ * Minor documentation fixes
48
+
49
+ == 1.16.2 / 2010-04-06
50
+ * Bugfix in Optimist::options. Thanks to Brian C. Thomas for pointing it out.
51
+
52
+ == 1.16.1 / 2010-04-05
53
+ * Bugfix in Optimist::die method introduced in last release.
54
+
55
+ == 1.16 / 2010-04-01
56
+ * Add Optimist::with_standard_exception_handling method for easing the use of Parser directly.
57
+ * Handle scientific notation in float arguments, thanks to Will Fitzgerald.
58
+ * Drop hoe dependency.
59
+
60
+ == 1.15 / 2009-09-30
61
+ * Don't raise an exception when out of short arguments (thanks to Rafael
62
+ Sevilla for pointing out how dumb this behavior was).
63
+
64
+ == 1.14 / 2009-06-19
65
+ * Make :multi arguments default to [], not nil, when not set on the commandline.
66
+ * Minor commenting and error message improvements
67
+
68
+ == 1.13 / 2009-03-16
69
+ * Fix parsing of "--longarg=<value with spaces>".
70
+
71
+ == 1.12 / 2009-01-30
72
+ * Fix some unit test failures in the last release. Should be more careful.
73
+ * Make default short options only be assigned *after* all user-specified
74
+ short options. Now there's a little less juggling to do when you just
75
+ want to specify a few short options.
76
+
77
+ == 1.11 / 2009-01-29
78
+ * Set <opt>_given keys in the results hash for options that were specified
79
+ on the commandline.
80
+
81
+ == 1.10.2 / 2008-10-23
82
+ * No longer try `stty size` for screen size detection. Just use curses, and
83
+ screen users will have to deal with the screen clearing.
84
+
85
+ == 1.10.1 / 2008-10-22
86
+ * Options hash now responds to method calls as well as standard hash lookup.
87
+ * Default values for multi-occurrence parameters now autoboxed.
88
+ * The relationship between multi-value, multi-occurrence, and default values
89
+ improved and explained.
90
+ * Documentation improvements.
91
+
92
+ == 1.10 / 2008-10-21
93
+ * Added :io type for parameters that point to IO streams (filenames, URIs, etc).
94
+ * For screen size detection, first try `stty size` before loading Curses.
95
+ * Improved documentation.
96
+
97
+ == 1.9 / 2008-08-20
98
+ * Added 'stop_on_unknown' command to stop parsing on any unknown argument.
99
+ This is useful for handling sub-commands when you don't know the entire
100
+ set of commands up front. (E.g. if the initial arguments can change it.)
101
+ * Added a :multi option for parameters, signifying that they can be specified
102
+ multiple times.
103
+ * Added :ints, :strings, :doubles, and :floats option types, which can take
104
+ multiple arguments.
105
+
106
+ == 1.8.2 / 2008-06-25
107
+ * Bugfix for #conflicts and #depends error messages
108
+
109
+ == 1.8.1 / 2008-06-24
110
+ * Bugfix for short option autocreation
111
+ * More aggressive documentation
112
+
113
+ == 1.8 / 2008-06-16
114
+ * Sub-command support via Parser#stop_on
115
+
116
+ == 1.7.2 / 2008-01-16
117
+ * Ruby 1.9-ify. Apparently this means replacing :'s with ;'s.
118
+
119
+ == 1.7.1 / 2008-01-07
120
+ * Documentation improvements
121
+
122
+ == 1.7 / 2007-06-17
123
+ * Fix incorrect error message for multiple missing required arguments
124
+ (thanks to Neill Zero)
125
+
126
+ == 1.6 / 2007-04-01
127
+ * Don't attempt curses screen-width magic unless running on a terminal.
128
+
129
+ == 1.5 / 2007-03-31
130
+ * --help and --version do the right thing even if the rest of the
131
+ command line is incorrect.
132
+ * Added #conflicts and #depends to model dependencies and exclusivity
133
+ between arguments.
134
+ * Minor bugfixes.
135
+
136
+ == 1.4 / 2007-03-26
137
+ * Disable short options with :short => :none.
138
+ * Minor bugfixes and error message improvements.
139
+
140
+ == 1.3 / 2007-01-31
141
+ * Wrap at (screen width - 1) instead of screen width.
142
+ * User can override --help and --version.
143
+ * Bugfix in handling of -v and -h.
144
+ * More tests to confirm the above.
145
+
146
+ == 1.2 / 2007-01-31
147
+ * Minor documentation tweaks.
148
+ * Removed hoe dependency.
149
+
150
+ == 1.1 / 2007-01-30
151
+ * Optimist::options now passes any arguments as block arguments. Since
152
+ instance variables are not properly captured by the block, this
153
+ makes it slightly less noisy to pass them in as local variables.
154
+ (A real-life use for _why's cloaker!)
155
+ * Help display now preserves original argument order.
156
+ * Optimist::die now also has a single string form in case death is not
157
+ due to a single argument.
158
+ * Parser#text now an alias for Parser#banner, and can be called
159
+ multiple times, with the output being placed in the right position
160
+ in the help text.
161
+ * Slightly more indicative formatting for parameterized arguments.
162
+
163
+ == 1.0 / 2007-01-29
164
+ * Initial release.
165
+
166
+ [2.1.3]: https://github.com/ManageIQ/optimist/compare/v2.1.2...v2.1.3
167
+ [2.1.2]: https://github.com/ManageIQ/optimist/compare/v2.1.1...v2.1.2
168
+ [2.1.1]: https://github.com/ManageIQ/optimist/compare/v2.1.0...v2.1.1
@@ -0,0 +1,60 @@
1
+ # optimist
2
+
3
+ http://manageiq.github.io/optimist/
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/optimist.svg)](http://badge.fury.io/rb/optimist)
6
+ [![Build Status](https://travis-ci.org/ManageIQ/optimist.svg)](https://travis-ci.org/ManageIQ/optimist)
7
+ [![Code Climate](https://codeclimate.com/github/ManageIQ/optimist/badges/gpa.svg)](https://codeclimate.com/github/ManageIQ/optimist)
8
+ [![Coverage Status](http://img.shields.io/coveralls/ManageIQ/optimist.svg)](https://coveralls.io/r/ManageIQ/optimist)
9
+ [![Dependency Status](https://gemnasium.com/ManageIQ/optimist.svg)](https://gemnasium.com/ManageIQ/optimist)
10
+
11
+ ## Documentation
12
+
13
+ - Quickstart: See `Optimist.options` and then `Optimist::Parser#opt`.
14
+ - Examples: http://manageiq.github.io/optimist/.
15
+ - Wiki: http://github.com/ManageIQ/optimist/wiki
16
+
17
+ ## Description
18
+
19
+ Optimist is a commandline option parser for Ruby that just gets out of your way.
20
+ One line of code per option is all you need to write. For that, you get a nice
21
+ automatically-generated help page, robust option parsing, and sensible defaults
22
+ for everything you don't specify.
23
+
24
+ ## Features
25
+
26
+ - Dirt-simple usage.
27
+ - Single file. Throw it in lib/ if you don't want to make it a Rubygem dependency.
28
+ - Sensible defaults. No tweaking necessary, much tweaking possible.
29
+ - Support for long options, short options, subcommands, and automatic type validation and
30
+ conversion.
31
+ - Automatic help message generation, wrapped to current screen width.
32
+
33
+ ## Requirements
34
+
35
+ * A burning desire to write less code.
36
+
37
+ ## Install
38
+
39
+ * gem install optimist
40
+
41
+ ## Synopsis
42
+
43
+ ```ruby
44
+ require 'optimist'
45
+ opts = Optimist::options do
46
+ opt :monkey, "Use monkey mode" # flag --monkey, default false
47
+ opt :name, "Monkey name", :type => :string # string --name <s>, default nil
48
+ opt :num_limbs, "Number of limbs", :default => 4 # integer --num-limbs <i>, default to 4
49
+ end
50
+
51
+ p opts # a hash: { :monkey=>false, :name=>nil, :num_limbs=>4, :help=>false }
52
+ ```
53
+
54
+ ## License
55
+
56
+ Copyright &copy; 2008-2014 [William Morgan](http://masanjin.net/).
57
+
58
+ Copyright &copy; 2014 Red Hat, Inc.
59
+
60
+ Optimist is released under the [MIT License](http://www.opensource.org/licenses/MIT).
@@ -0,0 +1,15 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << 'test'
8
+ t.pattern = "test/**/*_test.rb"
9
+ end
10
+
11
+ begin
12
+ require 'coveralls/rake/task'
13
+ Coveralls::RakeTask.new
14
+ rescue LoadError
15
+ end
@@ -0,0 +1,1014 @@
1
+ # lib/optimist.rb -- optimist command-line processing library
2
+ # Copyright (c) 2008-2014 William Morgan.
3
+ # Copyright (c) 2014 Red Hat, Inc.
4
+ # optimist is licensed under the MIT license.
5
+
6
+ require 'date'
7
+
8
+ module Optimist
9
+ # note: this is duplicated in gemspec
10
+ # please change over there too
11
+ VERSION = "3.0.0"
12
+
13
+ ## Thrown by Parser in the event of a commandline error. Not needed if
14
+ ## you're using the Optimist::options entry.
15
+ class CommandlineError < StandardError
16
+ attr_reader :error_code
17
+
18
+ def initialize(msg, error_code = nil)
19
+ super(msg)
20
+ @error_code = error_code
21
+ end
22
+ end
23
+
24
+ ## Thrown by Parser if the user passes in '-h' or '--help'. Handled
25
+ ## automatically by Optimist#options.
26
+ class HelpNeeded < StandardError
27
+ end
28
+
29
+ ## Thrown by Parser if the user passes in '-v' or '--version'. Handled
30
+ ## automatically by Optimist#options.
31
+ class VersionNeeded < StandardError
32
+ end
33
+
34
+ ## Regex for floating point numbers
35
+ FLOAT_RE = /^-?((\d+(\.\d+)?)|(\.\d+))([eE][-+]?[\d]+)?$/
36
+
37
+ ## Regex for parameters
38
+ PARAM_RE = /^-(-|\.$|[^\d\.])/
39
+
40
+ ## The commandline parser. In typical usage, the methods in this class
41
+ ## will be handled internally by Optimist::options. In this case, only the
42
+ ## #opt, #banner and #version, #depends, and #conflicts methods will
43
+ ## typically be called.
44
+ ##
45
+ ## If you want to instantiate this class yourself (for more complicated
46
+ ## argument-parsing logic), call #parse to actually produce the output hash,
47
+ ## and consider calling it from within
48
+ ## Optimist::with_standard_exception_handling.
49
+ class Parser
50
+
51
+ ## The registry is a class-instance-variable map of option aliases to their subclassed Option class.
52
+ @registry = {}
53
+
54
+ ## The Option subclasses are responsible for registering themselves using this function.
55
+ def self.register(lookup, klass)
56
+ @registry[lookup.to_sym] = klass
57
+ end
58
+
59
+ ## Gets the class from the registry.
60
+ ## Can be given either a class-name, e.g. Integer, a string, e.g "integer", or a symbol, e.g :integer
61
+ def self.registry_getopttype(type)
62
+ return nil unless type
63
+ if type.respond_to?(:name)
64
+ type = type.name
65
+ lookup = type.downcase.to_sym
66
+ else
67
+ lookup = type.to_sym
68
+ end
69
+ raise ArgumentError, "Unsupported argument type '#{type}', registry lookup '#{lookup}'" unless @registry.has_key?(lookup)
70
+ return @registry[lookup].new
71
+ end
72
+
73
+ INVALID_SHORT_ARG_REGEX = /[\d-]/ #:nodoc:
74
+
75
+ ## The values from the commandline that were not interpreted by #parse.
76
+ attr_reader :leftovers
77
+
78
+ ## The complete configuration hashes for each option. (Mainly useful
79
+ ## for testing.)
80
+ attr_reader :specs
81
+
82
+ ## A flag that determines whether or not to raise an error if the parser is passed one or more
83
+ ## options that were not registered ahead of time. If 'true', then the parser will simply
84
+ ## ignore options that it does not recognize.
85
+ attr_accessor :ignore_invalid_options
86
+
87
+ ## Initializes the parser, and instance-evaluates any block given.
88
+ def initialize(*a, &b)
89
+ @version = nil
90
+ @leftovers = []
91
+ @specs = {}
92
+ @long = {}
93
+ @short = {}
94
+ @order = []
95
+ @constraints = []
96
+ @stop_words = []
97
+ @stop_on_unknown = false
98
+ @educate_on_error = false
99
+ @synopsis = nil
100
+ @usage = nil
101
+
102
+ # instance_eval(&b) if b # can't take arguments
103
+ cloaker(&b).bind(self).call(*a) if b
104
+ end
105
+
106
+ ## Define an option. +name+ is the option name, a unique identifier
107
+ ## for the option that you will use internally, which should be a
108
+ ## symbol or a string. +desc+ is a string description which will be
109
+ ## displayed in help messages.
110
+ ##
111
+ ## Takes the following optional arguments:
112
+ ##
113
+ ## [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s.
114
+ ## [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+. Use :none: to not have a short value.
115
+ ## [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given.
116
+ ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Optimist::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+.
117
+ ## [+:required+] If set to +true+, the argument must be provided on the commandline.
118
+ ## [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.)
119
+ ##
120
+ ## Note that there are two types of argument multiplicity: an argument
121
+ ## can take multiple values, e.g. "--arg 1 2 3". An argument can also
122
+ ## be allowed to occur multiple times, e.g. "--arg 1 --arg 2".
123
+ ##
124
+ ## Arguments that take multiple values should have a +:type+ parameter
125
+ ## drawn from +MULTI_ARG_TYPES+ (e.g. +:strings+), or a +:default:+
126
+ ## value of an array of the correct type (e.g. [String]). The
127
+ ## value of this argument will be an array of the parameters on the
128
+ ## commandline.
129
+ ##
130
+ ## Arguments that can occur multiple times should be marked with
131
+ ## +:multi+ => +true+. The value of this argument will also be an array.
132
+ ## In contrast with regular non-multi options, if not specified on
133
+ ## the commandline, the default value will be [], not nil.
134
+ ##
135
+ ## These two attributes can be combined (e.g. +:type+ => +:strings+,
136
+ ## +:multi+ => +true+), in which case the value of the argument will be
137
+ ## an array of arrays.
138
+ ##
139
+ ## There's one ambiguous case to be aware of: when +:multi+: is true and a
140
+ ## +:default+ is set to an array (of something), it's ambiguous whether this
141
+ ## is a multi-value argument as well as a multi-occurrence argument.
142
+ ## In thise case, Optimist assumes that it's not a multi-value argument.
143
+ ## If you want a multi-value, multi-occurrence argument with a default
144
+ ## value, you must specify +:type+ as well.
145
+
146
+ def opt(name, desc = "", opts = {}, &b)
147
+ opts[:callback] ||= b if block_given?
148
+ opts[:desc] ||= desc
149
+
150
+ o = Option.create(name, desc, opts)
151
+
152
+ raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? o.name
153
+ raise ArgumentError, "long option name #{o.long.inspect} is already taken; please specify a (different) :long" if @long[o.long]
154
+ raise ArgumentError, "short option name #{o.short.inspect} is already taken; please specify a (different) :short" if @short[o.short]
155
+ @long[o.long] = o.name
156
+ @short[o.short] = o.name if o.short?
157
+ @specs[o.name] = o
158
+ @order << [:opt, o.name]
159
+ end
160
+
161
+ ## Sets the version string. If set, the user can request the version
162
+ ## on the commandline. Should probably be of the form "<program name>
163
+ ## <version number>".
164
+ def version(s = nil)
165
+ s ? @version = s : @version
166
+ end
167
+
168
+ ## Sets the usage string. If set the message will be printed as the
169
+ ## first line in the help (educate) output and ending in two new
170
+ ## lines.
171
+ def usage(s = nil)
172
+ s ? @usage = s : @usage
173
+ end
174
+
175
+ ## Adds a synopsis (command summary description) right below the
176
+ ## usage line, or as the first line if usage isn't specified.
177
+ def synopsis(s = nil)
178
+ s ? @synopsis = s : @synopsis
179
+ end
180
+
181
+ ## Adds text to the help display. Can be interspersed with calls to
182
+ ## #opt to build a multi-section help page.
183
+ def banner(s)
184
+ @order << [:text, s]
185
+ end
186
+ alias_method :text, :banner
187
+
188
+ ## Marks two (or more!) options as requiring each other. Only handles
189
+ ## undirected (i.e., mutual) dependencies. Directed dependencies are
190
+ ## better modeled with Optimist::die.
191
+ def depends(*syms)
192
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
193
+ @constraints << [:depends, syms]
194
+ end
195
+
196
+ ## Marks two (or more!) options as conflicting.
197
+ def conflicts(*syms)
198
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
199
+ @constraints << [:conflicts, syms]
200
+ end
201
+
202
+ ## Defines a set of words which cause parsing to terminate when
203
+ ## encountered, such that any options to the left of the word are
204
+ ## parsed as usual, and options to the right of the word are left
205
+ ## intact.
206
+ ##
207
+ ## A typical use case would be for subcommand support, where these
208
+ ## would be set to the list of subcommands. A subsequent Optimist
209
+ ## invocation would then be used to parse subcommand options, after
210
+ ## shifting the subcommand off of ARGV.
211
+ def stop_on(*words)
212
+ @stop_words = [*words].flatten
213
+ end
214
+
215
+ ## Similar to #stop_on, but stops on any unknown word when encountered
216
+ ## (unless it is a parameter for an argument). This is useful for
217
+ ## cases where you don't know the set of subcommands ahead of time,
218
+ ## i.e., without first parsing the global options.
219
+ def stop_on_unknown
220
+ @stop_on_unknown = true
221
+ end
222
+
223
+ ## Instead of displaying "Try --help for help." on an error
224
+ ## display the usage (via educate)
225
+ def educate_on_error
226
+ @educate_on_error = true
227
+ end
228
+
229
+ ## Parses the commandline. Typically called by Optimist::options,
230
+ ## but you can call it directly if you need more control.
231
+ ##
232
+ ## throws CommandlineError, HelpNeeded, and VersionNeeded exceptions.
233
+ def parse(cmdline = ARGV)
234
+ vals = {}
235
+ required = {}
236
+
237
+ opt :version, "Print version and exit" if @version && ! (@specs[:version] || @long["version"])
238
+ opt :help, "Show this message" unless @specs[:help] || @long["help"]
239
+
240
+ @specs.each do |sym, opts|
241
+ required[sym] = true if opts.required?
242
+ vals[sym] = opts.default
243
+ vals[sym] = [] if opts.multi && !opts.default # multi arguments default to [], not nil
244
+ end
245
+
246
+ resolve_default_short_options!
247
+
248
+ ## resolve symbols
249
+ given_args = {}
250
+ @leftovers = each_arg cmdline do |arg, params|
251
+ ## handle --no- forms
252
+ arg, negative_given = if arg =~ /^--no-([^-]\S*)$/
253
+ ["--#{$1}", true]
254
+ else
255
+ [arg, false]
256
+ end
257
+
258
+ sym = case arg
259
+ when /^-([^-])$/ then @short[$1]
260
+ when /^--([^-]\S*)$/ then @long[$1] || @long["no-#{$1}"]
261
+ else raise CommandlineError, "invalid argument syntax: '#{arg}'"
262
+ end
263
+
264
+ sym = nil if arg =~ /--no-/ # explicitly invalidate --no-no- arguments
265
+
266
+ next nil if ignore_invalid_options && !sym
267
+ raise CommandlineError, "unknown argument '#{arg}'" unless sym
268
+
269
+ if given_args.include?(sym) && !@specs[sym].multi?
270
+ raise CommandlineError, "option '#{arg}' specified multiple times"
271
+ end
272
+
273
+ given_args[sym] ||= {}
274
+ given_args[sym][:arg] = arg
275
+ given_args[sym][:negative_given] = negative_given
276
+ given_args[sym][:params] ||= []
277
+
278
+ # The block returns the number of parameters taken.
279
+ num_params_taken = 0
280
+
281
+ unless params.empty?
282
+ if @specs[sym].single_arg?
283
+ given_args[sym][:params] << params[0, 1] # take the first parameter
284
+ num_params_taken = 1
285
+ elsif @specs[sym].multi_arg?
286
+ given_args[sym][:params] << params # take all the parameters
287
+ num_params_taken = params.size
288
+ end
289
+ end
290
+
291
+ num_params_taken
292
+ end
293
+
294
+ ## check for version and help args
295
+ raise VersionNeeded if given_args.include? :version
296
+ raise HelpNeeded if given_args.include? :help
297
+
298
+ ## check constraint satisfaction
299
+ @constraints.each do |type, syms|
300
+ constraint_sym = syms.find { |sym| given_args[sym] }
301
+ next unless constraint_sym
302
+
303
+ case type
304
+ when :depends
305
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym].long} requires --#{@specs[sym].long}" unless given_args.include? sym }
306
+ when :conflicts
307
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym].long} conflicts with --#{@specs[sym].long}" if given_args.include?(sym) && (sym != constraint_sym) }
308
+ end
309
+ end
310
+
311
+ required.each do |sym, val|
312
+ raise CommandlineError, "option --#{@specs[sym].long} must be specified" unless given_args.include? sym
313
+ end
314
+
315
+ ## parse parameters
316
+ given_args.each do |sym, given_data|
317
+ arg, params, negative_given = given_data.values_at :arg, :params, :negative_given
318
+
319
+ opts = @specs[sym]
320
+ if params.empty? && !opts.flag?
321
+ raise CommandlineError, "option '#{arg}' needs a parameter" unless opts.default
322
+ params << (opts.array_default? ? opts.default.clone : [opts.default])
323
+ end
324
+
325
+ vals["#{sym}_given".intern] = true # mark argument as specified on the commandline
326
+
327
+ vals[sym] = opts.parse(params, negative_given)
328
+
329
+ if opts.single_arg?
330
+ if opts.multi? # multiple options, each with a single parameter
331
+ vals[sym] = vals[sym].map { |p| p[0] }
332
+ else # single parameter
333
+ vals[sym] = vals[sym][0][0]
334
+ end
335
+ elsif opts.multi_arg? && !opts.multi?
336
+ vals[sym] = vals[sym][0] # single option, with multiple parameters
337
+ end
338
+ # else: multiple options, with multiple parameters
339
+
340
+ opts.callback.call(vals[sym]) if opts.callback
341
+ end
342
+
343
+ ## modify input in place with only those
344
+ ## arguments we didn't process
345
+ cmdline.clear
346
+ @leftovers.each { |l| cmdline << l }
347
+
348
+ ## allow openstruct-style accessors
349
+ class << vals
350
+ def method_missing(m, *_args)
351
+ self[m] || self[m.to_s]
352
+ end
353
+ end
354
+ vals
355
+ end
356
+
357
+ ## Print the help message to +stream+.
358
+ def educate(stream = $stdout)
359
+ width # hack: calculate it now; otherwise we have to be careful not to
360
+ # call this unless the cursor's at the beginning of a line.
361
+
362
+ left = {}
363
+ @specs.each { |name, spec| left[name] = spec.educate }
364
+
365
+ leftcol_width = left.values.map(&:length).max || 0
366
+ rightcol_start = leftcol_width + 6 # spaces
367
+
368
+ unless @order.size > 0 && @order.first.first == :text
369
+ command_name = File.basename($0).gsub(/\.[^.]+$/, '')
370
+ stream.puts "Usage: #{command_name} #{@usage}\n" if @usage
371
+ stream.puts "#{@synopsis}\n" if @synopsis
372
+ stream.puts if @usage || @synopsis
373
+ stream.puts "#{@version}\n" if @version
374
+ stream.puts "Options:"
375
+ end
376
+
377
+ @order.each do |what, opt|
378
+ if what == :text
379
+ stream.puts wrap(opt)
380
+ next
381
+ end
382
+
383
+ spec = @specs[opt]
384
+ stream.printf " %-#{leftcol_width}s ", left[opt]
385
+ desc = spec.description_with_default
386
+
387
+ stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
388
+ end
389
+ end
390
+
391
+ def width #:nodoc:
392
+ @width ||= if $stdout.tty?
393
+ begin
394
+ require 'io/console'
395
+ w = IO.console.winsize.last
396
+ w.to_i > 0 ? w : 80
397
+ rescue LoadError, NoMethodError, Errno::ENOTTY, Errno::EBADF, Errno::EINVAL
398
+ legacy_width
399
+ end
400
+ else
401
+ 80
402
+ end
403
+ end
404
+
405
+ def legacy_width
406
+ # Support for older Rubies where io/console is not available
407
+ `tput cols`.to_i
408
+ rescue Errno::ENOENT
409
+ 80
410
+ end
411
+ private :legacy_width
412
+
413
+ def wrap(str, opts = {}) # :nodoc:
414
+ if str == ""
415
+ [""]
416
+ else
417
+ inner = false
418
+ str.split("\n").map do |s|
419
+ line = wrap_line s, opts.merge(:inner => inner)
420
+ inner = true
421
+ line
422
+ end.flatten
423
+ end
424
+ end
425
+
426
+ ## The per-parser version of Optimist::die (see that for documentation).
427
+ def die(arg, msg = nil, error_code = nil)
428
+ msg, error_code = nil, msg if msg.kind_of?(Integer)
429
+ if msg
430
+ $stderr.puts "Error: argument --#{@specs[arg].long} #{msg}."
431
+ else
432
+ $stderr.puts "Error: #{arg}."
433
+ end
434
+ if @educate_on_error
435
+ $stderr.puts
436
+ educate $stderr
437
+ else
438
+ $stderr.puts "Try --help for help."
439
+ end
440
+ exit(error_code || -1)
441
+ end
442
+
443
+ private
444
+
445
+ ## yield successive arg, parameter pairs
446
+ def each_arg(args)
447
+ remains = []
448
+ i = 0
449
+
450
+ until i >= args.length
451
+ return remains += args[i..-1] if @stop_words.member? args[i]
452
+ case args[i]
453
+ when /^--$/ # arg terminator
454
+ return remains += args[(i + 1)..-1]
455
+ when /^--(\S+?)=(.*)$/ # long argument with equals
456
+ num_params_taken = yield "--#{$1}", [$2]
457
+ if num_params_taken.nil?
458
+ remains << args[i]
459
+ if @stop_on_unknown
460
+ return remains += args[i + 1..-1]
461
+ end
462
+ end
463
+ i += 1
464
+ when /^--(\S+)$/ # long argument
465
+ params = collect_argument_parameters(args, i + 1)
466
+ num_params_taken = yield args[i], params
467
+
468
+ if num_params_taken.nil?
469
+ remains << args[i]
470
+ if @stop_on_unknown
471
+ return remains += args[i + 1..-1]
472
+ end
473
+ else
474
+ i += num_params_taken
475
+ end
476
+ i += 1
477
+ when /^-(\S+)$/ # one or more short arguments
478
+ short_remaining = ""
479
+ shortargs = $1.split(//)
480
+ shortargs.each_with_index do |a, j|
481
+ if j == (shortargs.length - 1)
482
+ params = collect_argument_parameters(args, i + 1)
483
+
484
+ num_params_taken = yield "-#{a}", params
485
+ unless num_params_taken
486
+ short_remaining << a
487
+ if @stop_on_unknown
488
+ remains << "-#{short_remaining}"
489
+ return remains += args[i + 1..-1]
490
+ end
491
+ else
492
+ i += num_params_taken
493
+ end
494
+ else
495
+ unless yield "-#{a}", []
496
+ short_remaining << a
497
+ if @stop_on_unknown
498
+ short_remaining += shortargs[j + 1..-1].join
499
+ remains << "-#{short_remaining}"
500
+ return remains += args[i + 1..-1]
501
+ end
502
+ end
503
+ end
504
+ end
505
+
506
+ unless short_remaining.empty?
507
+ remains << "-#{short_remaining}"
508
+ end
509
+ i += 1
510
+ else
511
+ if @stop_on_unknown
512
+ return remains += args[i..-1]
513
+ else
514
+ remains << args[i]
515
+ i += 1
516
+ end
517
+ end
518
+ end
519
+
520
+ remains
521
+ end
522
+
523
+ def collect_argument_parameters(args, start_at)
524
+ params = []
525
+ pos = start_at
526
+ while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do
527
+ params << args[pos]
528
+ pos += 1
529
+ end
530
+ params
531
+ end
532
+
533
+ def resolve_default_short_options!
534
+ @order.each do |type, name|
535
+ opts = @specs[name]
536
+ next if type != :opt || opts.short
537
+
538
+ c = opts.long.split(//).find { |d| d !~ INVALID_SHORT_ARG_REGEX && !@short.member?(d) }
539
+ if c # found a character to use
540
+ opts.short = c
541
+ @short[c] = name
542
+ end
543
+ end
544
+ end
545
+
546
+ def wrap_line(str, opts = {})
547
+ prefix = opts[:prefix] || 0
548
+ width = opts[:width] || (self.width - 1)
549
+ start = 0
550
+ ret = []
551
+ until start > str.length
552
+ nextt =
553
+ if start + width >= str.length
554
+ str.length
555
+ else
556
+ x = str.rindex(/\s/, start + width)
557
+ x = str.index(/\s/, start) if x && x < start
558
+ x || str.length
559
+ end
560
+ ret << ((ret.empty? && !opts[:inner]) ? "" : " " * prefix) + str[start...nextt]
561
+ start = nextt + 1
562
+ end
563
+ ret
564
+ end
565
+
566
+ ## instance_eval but with ability to handle block arguments
567
+ ## thanks to _why: http://redhanded.hobix.com/inspect/aBlockCostume.html
568
+ def cloaker(&b)
569
+ (class << self; self; end).class_eval do
570
+ define_method :cloaker_, &b
571
+ meth = instance_method :cloaker_
572
+ remove_method :cloaker_
573
+ meth
574
+ end
575
+ end
576
+ end
577
+
578
+ class Option
579
+
580
+ attr_accessor :name, :short, :long, :default
581
+ attr_writer :multi_given
582
+
583
+ def initialize
584
+ @long = nil
585
+ @short = nil
586
+ @name = nil
587
+ @multi_given = false
588
+ @hidden = false
589
+ @default = nil
590
+ @optshash = Hash.new()
591
+ end
592
+
593
+ def opts (key)
594
+ @optshash[key]
595
+ end
596
+
597
+ def opts= (o)
598
+ @optshash = o
599
+ end
600
+
601
+ ## Indicates a flag option, which is an option without an argument
602
+ def flag? ; false ; end
603
+ def single_arg?
604
+ !self.multi_arg? && !self.flag?
605
+ end
606
+
607
+ def multi ; @multi_given ; end
608
+ alias multi? multi
609
+
610
+ ## Indicates that this is a multivalued (Array type) argument
611
+ def multi_arg? ; false ; end
612
+ ## note: Option-Types with both multi_arg? and flag? false are single-parameter (normal) options.
613
+
614
+ def array_default? ; self.default.kind_of?(Array) ; end
615
+
616
+ def short? ; short && short != :none ; end
617
+
618
+ def callback ; opts(:callback) ; end
619
+ def desc ; opts(:desc) ; end
620
+
621
+ def required? ; opts(:required) ; end
622
+
623
+ def parse (_paramlist, _neg_given)
624
+ raise NotImplementedError, "parse must be overridden for newly registered type"
625
+ end
626
+
627
+ # provide type-format string. default to empty, but user should probably override it
628
+ def type_format ; "" ; end
629
+
630
+ def educate
631
+ (short? ? "-#{short}, " : "") + "--#{long}" + type_format + (flag? && default ? ", --no-#{long}" : "")
632
+ end
633
+
634
+ ## Format the educate-line description including the default-value(s)
635
+ def description_with_default
636
+ return desc unless default
637
+ default_s = case default
638
+ when $stdout then '<stdout>'
639
+ when $stdin then '<stdin>'
640
+ when $stderr then '<stderr>'
641
+ when Array
642
+ default.join(', ')
643
+ else
644
+ default.to_s
645
+ end
646
+ defword = desc.end_with?('.') ? 'Default' : 'default'
647
+ return "#{desc} (#{defword}: #{default_s})"
648
+ end
649
+
650
+ ## Provide a way to register symbol aliases to the Parser
651
+ def self.register_alias(*alias_keys)
652
+ alias_keys.each do |alias_key|
653
+ # pass in the alias-key and the class
654
+ Parser.register(alias_key, self)
655
+ end
656
+ end
657
+
658
+ ## Factory class methods ...
659
+
660
+ # Determines which type of object to create based on arguments passed
661
+ # to +Optimist::opt+. This is trickier in Optimist, than other cmdline
662
+ # parsers (e.g. Slop) because we allow the +default:+ to be able to
663
+ # set the option's type.
664
+ def self.create(name, desc="", opts={}, settings={})
665
+
666
+ opttype = Optimist::Parser.registry_getopttype(opts[:type])
667
+ opttype_from_default = get_klass_from_default(opts, opttype)
668
+
669
+ raise ArgumentError, ":type specification and default type don't match (default type is #{opttype_from_default.class})" if opttype && opttype_from_default && (opttype.class != opttype_from_default.class)
670
+
671
+ opt_inst = (opttype || opttype_from_default || Optimist::BooleanOption.new)
672
+
673
+ ## fill in :long
674
+ opt_inst.long = handle_long_opt(opts[:long], name)
675
+
676
+ ## fill in :short
677
+ opt_inst.short = handle_short_opt(opts[:short])
678
+
679
+ ## fill in :multi
680
+ multi_given = opts[:multi] || false
681
+ opt_inst.multi_given = multi_given
682
+
683
+ ## fill in :default for flags
684
+ defvalue = opts[:default] || opt_inst.default
685
+
686
+ ## autobox :default for :multi (multi-occurrence) arguments
687
+ defvalue = [defvalue] if defvalue && multi_given && !defvalue.kind_of?(Array)
688
+ opt_inst.default = defvalue
689
+ opt_inst.name = name
690
+ opt_inst.opts = opts
691
+ opt_inst
692
+ end
693
+
694
+ private
695
+
696
+ def self.get_type_from_disdef(optdef, opttype, disambiguated_default)
697
+ if disambiguated_default.is_a? Array
698
+ return(optdef.first.class.name.downcase + "s") if !optdef.empty?
699
+ if opttype
700
+ raise ArgumentError, "multiple argument type must be plural" unless opttype.multi_arg?
701
+ return nil
702
+ else
703
+ raise ArgumentError, "multiple argument type cannot be deduced from an empty array"
704
+ end
705
+ end
706
+ return disambiguated_default.class.name.downcase
707
+ end
708
+
709
+ def self.get_klass_from_default(opts, opttype)
710
+ ## for options with :multi => true, an array default doesn't imply
711
+ ## a multi-valued argument. for that you have to specify a :type
712
+ ## as well. (this is how we disambiguate an ambiguous situation;
713
+ ## see the docs for Parser#opt for details.)
714
+
715
+ disambiguated_default = if opts[:multi] && opts[:default].is_a?(Array) && opttype.nil?
716
+ opts[:default].first
717
+ else
718
+ opts[:default]
719
+ end
720
+
721
+ return nil if disambiguated_default.nil?
722
+ type_from_default = get_type_from_disdef(opts[:default], opttype, disambiguated_default)
723
+ return Optimist::Parser.registry_getopttype(type_from_default)
724
+ end
725
+
726
+ def self.handle_long_opt(lopt, name)
727
+ lopt = lopt ? lopt.to_s : name.to_s.gsub("_", "-")
728
+ lopt = case lopt
729
+ when /^--([^-].*)$/ then $1
730
+ when /^[^-]/ then lopt
731
+ else raise ArgumentError, "invalid long option name #{lopt.inspect}"
732
+ end
733
+ end
734
+
735
+ def self.handle_short_opt(sopt)
736
+ sopt = sopt.to_s if sopt && sopt != :none
737
+ sopt = case sopt
738
+ when /^-(.)$/ then $1
739
+ when nil, :none, /^.$/ then sopt
740
+ else raise ArgumentError, "invalid short option name '#{sopt.inspect}'"
741
+ end
742
+
743
+ if sopt
744
+ raise ArgumentError, "a short option name can't be a number or a dash" if sopt =~ ::Optimist::Parser::INVALID_SHORT_ARG_REGEX
745
+ end
746
+ return sopt
747
+ end
748
+
749
+ end
750
+
751
+ # Flag option. Has no arguments. Can be negated with "no-".
752
+ class BooleanOption < Option
753
+ register_alias :flag, :bool, :boolean, :trueclass, :falseclass
754
+ def initialize
755
+ super()
756
+ @default = false
757
+ end
758
+ def flag? ; true ; end
759
+ def parse(_paramlist, neg_given)
760
+ return(self.name.to_s =~ /^no_/ ? neg_given : !neg_given)
761
+ end
762
+ end
763
+
764
+ # Floating point number option class.
765
+ class FloatOption < Option
766
+ register_alias :float, :double
767
+ def type_format ; "=<f>" ; end
768
+ def parse(paramlist, _neg_given)
769
+ paramlist.map do |pg|
770
+ pg.map do |param|
771
+ raise CommandlineError, "option '#{self.name}' needs a floating-point number" unless param.is_a?(Numeric) || param =~ FLOAT_RE
772
+ param.to_f
773
+ end
774
+ end
775
+ end
776
+ end
777
+
778
+ # Integer number option class.
779
+ class IntegerOption < Option
780
+ register_alias :int, :integer, :fixnum
781
+ def type_format ; "=<i>" ; end
782
+ def parse(paramlist, _neg_given)
783
+ paramlist.map do |pg|
784
+ pg.map do |param|
785
+ raise CommandlineError, "option '#{self.name}' needs an integer" unless param.is_a?(Numeric) || param =~ /^-?[\d_]+$/
786
+ param.to_i
787
+ end
788
+ end
789
+ end
790
+ end
791
+
792
+ # Option class for handling IO objects and URLs.
793
+ # Note that this will return the file-handle, not the file-name
794
+ # in the case of file-paths given to it.
795
+ class IOOption < Option
796
+ register_alias :io
797
+ def type_format ; "=<filename/uri>" ; end
798
+ def parse(paramlist, _neg_given)
799
+ paramlist.map do |pg|
800
+ pg.map do |param|
801
+ if param =~ /^(stdin|-)$/i
802
+ $stdin
803
+ else
804
+ require 'open-uri'
805
+ begin
806
+ open param
807
+ rescue SystemCallError => e
808
+ raise CommandlineError, "file or url for option '#{self.name}' cannot be opened: #{e.message}"
809
+ end
810
+ end
811
+ end
812
+ end
813
+ end
814
+ end
815
+
816
+ # Option class for handling Strings.
817
+ class StringOption < Option
818
+ register_alias :string
819
+ def type_format ; "=<s>" ; end
820
+ def parse(paramlist, _neg_given)
821
+ paramlist.map { |pg| pg.map(&:to_s) }
822
+ end
823
+ end
824
+
825
+ # Option for dates. Uses Chronic if it exists.
826
+ class DateOption < Option
827
+ register_alias :date
828
+ def type_format ; "=<date>" ; end
829
+ def parse(paramlist, _neg_given)
830
+ paramlist.map do |pg|
831
+ pg.map do |param|
832
+ next param if param.is_a?(Date)
833
+ begin
834
+ begin
835
+ require 'chronic'
836
+ time = Chronic.parse(param)
837
+ rescue LoadError
838
+ # chronic is not available
839
+ end
840
+ time ? Date.new(time.year, time.month, time.day) : Date.parse(param)
841
+ rescue ArgumentError
842
+ raise CommandlineError, "option '#{self.name}' needs a date"
843
+ end
844
+ end
845
+ end
846
+ end
847
+ end
848
+
849
+ ### MULTI_OPT_TYPES :
850
+ ## The set of values that indicate a multiple-parameter option (i.e., that
851
+ ## takes multiple space-separated values on the commandline) when passed as
852
+ ## the +:type+ parameter of #opt.
853
+
854
+ # Option class for handling multiple Integers
855
+ class IntegerArrayOption < IntegerOption
856
+ register_alias :fixnums, :ints, :integers
857
+ def type_format ; "=<i+>" ; end
858
+ def multi_arg? ; true ; end
859
+ end
860
+
861
+ # Option class for handling multiple Floats
862
+ class FloatArrayOption < FloatOption
863
+ register_alias :doubles, :floats
864
+ def type_format ; "=<f+>" ; end
865
+ def multi_arg? ; true ; end
866
+ end
867
+
868
+ # Option class for handling multiple Strings
869
+ class StringArrayOption < StringOption
870
+ register_alias :strings
871
+ def type_format ; "=<s+>" ; end
872
+ def multi_arg? ; true ; end
873
+ end
874
+
875
+ # Option class for handling multiple dates
876
+ class DateArrayOption < DateOption
877
+ register_alias :dates
878
+ def type_format ; "=<date+>" ; end
879
+ def multi_arg? ; true ; end
880
+ end
881
+
882
+ # Option class for handling Files/URLs via 'open'
883
+ class IOArrayOption < IOOption
884
+ register_alias :ios
885
+ def type_format ; "=<filename/uri+>" ; end
886
+ def multi_arg? ; true ; end
887
+ end
888
+
889
+ ## The easy, syntactic-sugary entry method into Optimist. Creates a Parser,
890
+ ## passes the block to it, then parses +args+ with it, handling any errors or
891
+ ## requests for help or version information appropriately (and then exiting).
892
+ ## Modifies +args+ in place. Returns a hash of option values.
893
+ ##
894
+ ## The block passed in should contain zero or more calls to +opt+
895
+ ## (Parser#opt), zero or more calls to +text+ (Parser#text), and
896
+ ## probably a call to +version+ (Parser#version).
897
+ ##
898
+ ## The returned block contains a value for every option specified with
899
+ ## +opt+. The value will be the value given on the commandline, or the
900
+ ## default value if the option was not specified on the commandline. For
901
+ ## every option specified on the commandline, a key "<option
902
+ ## name>_given" will also be set in the hash.
903
+ ##
904
+ ## Example:
905
+ ##
906
+ ## require 'optimist'
907
+ ## opts = Optimist::options do
908
+ ## opt :monkey, "Use monkey mode" # a flag --monkey, defaulting to false
909
+ ## opt :name, "Monkey name", :type => :string # a string --name <s>, defaulting to nil
910
+ ## opt :num_limbs, "Number of limbs", :default => 4 # an integer --num-limbs <i>, defaulting to 4
911
+ ## end
912
+ ##
913
+ ## ## if called with no arguments
914
+ ## p opts # => {:monkey=>false, :name=>nil, :num_limbs=>4, :help=>false}
915
+ ##
916
+ ## ## if called with --monkey
917
+ ## p opts # => {:monkey=>true, :name=>nil, :num_limbs=>4, :help=>false, :monkey_given=>true}
918
+ ##
919
+ ## See more examples at http://optimist.rubyforge.org.
920
+ def options(args = ARGV, *a, &b)
921
+ @last_parser = Parser.new(*a, &b)
922
+ with_standard_exception_handling(@last_parser) { @last_parser.parse args }
923
+ end
924
+
925
+ ## If Optimist::options doesn't do quite what you want, you can create a Parser
926
+ ## object and call Parser#parse on it. That method will throw CommandlineError,
927
+ ## HelpNeeded and VersionNeeded exceptions when necessary; if you want to
928
+ ## have these handled for you in the standard manner (e.g. show the help
929
+ ## and then exit upon an HelpNeeded exception), call your code from within
930
+ ## a block passed to this method.
931
+ ##
932
+ ## Note that this method will call System#exit after handling an exception!
933
+ ##
934
+ ## Usage example:
935
+ ##
936
+ ## require 'optimist'
937
+ ## p = Optimist::Parser.new do
938
+ ## opt :monkey, "Use monkey mode" # a flag --monkey, defaulting to false
939
+ ## opt :goat, "Use goat mode", :default => true # a flag --goat, defaulting to true
940
+ ## end
941
+ ##
942
+ ## opts = Optimist::with_standard_exception_handling p do
943
+ ## o = p.parse ARGV
944
+ ## raise Optimist::HelpNeeded if ARGV.empty? # show help screen
945
+ ## o
946
+ ## end
947
+ ##
948
+ ## Requires passing in the parser object.
949
+
950
+ def with_standard_exception_handling(parser)
951
+ yield
952
+ rescue CommandlineError => e
953
+ parser.die(e.message, nil, e.error_code)
954
+ rescue HelpNeeded
955
+ parser.educate
956
+ exit
957
+ rescue VersionNeeded
958
+ puts parser.version
959
+ exit
960
+ end
961
+
962
+ ## Informs the user that their usage of 'arg' was wrong, as detailed by
963
+ ## 'msg', and dies. Example:
964
+ ##
965
+ ## options do
966
+ ## opt :volume, :default => 0.0
967
+ ## end
968
+ ##
969
+ ## die :volume, "too loud" if opts[:volume] > 10.0
970
+ ## die :volume, "too soft" if opts[:volume] < 0.1
971
+ ##
972
+ ## In the one-argument case, simply print that message, a notice
973
+ ## about -h, and die. Example:
974
+ ##
975
+ ## options do
976
+ ## opt :whatever # ...
977
+ ## end
978
+ ##
979
+ ## Optimist::die "need at least one filename" if ARGV.empty?
980
+ ##
981
+ ## An exit code can be provide if needed
982
+ ##
983
+ ## Optimist::die "need at least one filename", -2 if ARGV.empty?
984
+ def die(arg, msg = nil, error_code = nil)
985
+ if @last_parser
986
+ @last_parser.die arg, msg, error_code
987
+ else
988
+ raise ArgumentError, "Optimist::die can only be called after Optimist::options"
989
+ end
990
+ end
991
+
992
+ ## Displays the help message and dies. Example:
993
+ ##
994
+ ## options do
995
+ ## opt :volume, :default => 0.0
996
+ ## banner <<-EOS
997
+ ## Usage:
998
+ ## #$0 [options] <name>
999
+ ## where [options] are:
1000
+ ## EOS
1001
+ ## end
1002
+ ##
1003
+ ## Optimist::educate if ARGV.empty?
1004
+ def educate
1005
+ if @last_parser
1006
+ @last_parser.educate
1007
+ exit
1008
+ else
1009
+ raise ArgumentError, "Optimist::educate can only be called after Optimist::options"
1010
+ end
1011
+ end
1012
+
1013
+ module_function :options, :die, :educate, :with_standard_exception_handling
1014
+ end # module