opt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4c5e2c614056045ba24f44b7fe368b76bc3c8ae7
4
+ data.tar.gz: 42212a7f059c754fd846bbcbd9f5ad6823ca8a57
5
+ SHA512:
6
+ metadata.gz: 29820dfd8267e9fe3e95586b74738e6094949ca4b4d95810522e9a7bf3f1ddc8ff7ae1c55b91f0f3422440a18d4e49c33f43fa6a085028a2e337752871c137c2
7
+ data.tar.gz: 9d70b7861cc491645a317f7d608f503682afa42f80951a4163662efd7a78b704d67a8e835b0ecd83c1ca4b20c12bc18deae2479e80e2f15005f76d15ae17863e
data/LICENSE.txt ADDED
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # Opt
2
+
3
+ An option parsing library.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'opt'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install opt
18
+
19
+ ## Usage
20
+
21
+ Simply specify your options:
22
+
23
+ ```ruby
24
+ opt = Opt.new
25
+ opt.option '--help, -h'
26
+ opt.option '--version, -v'
27
+
28
+ result = opt.parse %w(-h)
29
+ result.help? #=> true
30
+ result.version? #=> nil
31
+ ```
32
+
33
+ You can also specify subcommands, the number of arguments for a switch or a text option. You can further specify a custom name with the opts hash, otherwise the name of the first switch will be used.
34
+
35
+ ```ruby
36
+ opt = Opt.new
37
+ opt.command 'merge' do |cmd|
38
+ cmd.option '--out, -O', nargs: 1
39
+ cmd.option 'file', name: :files, nargs: '+'
40
+ end
41
+
42
+ result = opt.parse %w(merge --out out.txt file1.txt file2.txt)
43
+ result.out #=> "out.txt"
44
+ result.files #=> ["file1.txt", "file2.txt"]
45
+ ```
46
+
47
+ Different styles are supported:
48
+
49
+ ```ruby
50
+ opt = Opt.new
51
+ opt.option '--level, -l', nargs: 1
52
+
53
+ opt.parse(%w(--level 5)).level #=> "5"
54
+ opt.parse(%w(--level=5)).level #=> "5"
55
+ opt.parse(%w(-l 5)).level #=> "5"
56
+ opt.parse(%w(-l5)).level #=> "5"
57
+ ```
58
+
59
+ See API documentation and specs for more examples and configuration option.
60
+
61
+ ## ToDos
62
+
63
+ * Argument type parsing
64
+ * Option block argument processing
65
+ * GNU Flags (--no-xy, --with/without-xy)
66
+ * rc-file loading (?)
67
+
68
+ ## Contributing
69
+
70
+ 1. Fork it (http://github.com/jgraichen/opt/fork)
71
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
72
+ 3. Add specs so that I do not break your feature later.
73
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
74
+ 5. Push to the branch (`git push origin my-new-feature`)
75
+ 6. Create new Pull Request
76
+
77
+ ## License
78
+
79
+ Copyright (C) 2014 Jan Graichen
80
+
81
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
82
+
83
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
84
+
85
+ You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,107 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6
+ <title>
7
+ File: README
8
+
9
+ &mdash; Documentation by YARD 0.8.7.4
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ hasFrames = window.top.frames.main ? true : false;
19
+ relpath = '';
20
+ framesUrl = "frames.html#!file.README.html";
21
+ </script>
22
+
23
+
24
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
25
+
26
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
27
+
28
+
29
+ </head>
30
+ <body>
31
+ <div id="header">
32
+ <div id="menu">
33
+
34
+ <a href="_index.html">Index</a> &raquo;
35
+ <span class="title">File: README</span>
36
+
37
+
38
+ <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
39
+ </div>
40
+
41
+ <div id="search">
42
+
43
+ <a class="full_list_link" id="class_list_link"
44
+ href="class_list.html">
45
+ Class List
46
+ </a>
47
+
48
+ <a class="full_list_link" id="method_list_link"
49
+ href="method_list.html">
50
+ Method List
51
+ </a>
52
+
53
+ <a class="full_list_link" id="file_list_link"
54
+ href="file_list.html">
55
+ File List
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <iframe id="search_frame"></iframe>
63
+
64
+ <div id="content"><div id='filecontents'><h1>Opt</h1>
65
+
66
+ <p>TODO: Write a gem description</p>
67
+
68
+ <h2>Installation</h2>
69
+
70
+ <p>Add this line to your application&#39;s Gemfile:</p>
71
+
72
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_gem'>gem</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>opt</span><span class='tstring_end'>&#39;</span></span>
73
+ </code></pre>
74
+
75
+ <p>And then execute:</p>
76
+
77
+ <pre class="code ruby"><code class="ruby">$ bundle
78
+ </code></pre>
79
+
80
+ <p>Or install it yourself as:</p>
81
+
82
+ <pre class="code ruby"><code class="ruby">$ gem install opt
83
+ </code></pre>
84
+
85
+ <h2>Usage</h2>
86
+
87
+ <p>TODO: Write usage instructions here</p>
88
+
89
+ <h2>Contributing</h2>
90
+
91
+ <ol>
92
+ <li>Fork it ( <a href="http://github.com/">http://github.com/</a><my-github-username>/opt/fork )</li>
93
+ <li>Create your feature branch (<code>git checkout -b my-new-feature</code>)</li>
94
+ <li>Commit your changes (<code>git commit -am &#39;Add some feature&#39;</code>)</li>
95
+ <li>Push to the branch (<code>git push origin my-new-feature</code>)</li>
96
+ <li>Create new Pull Request</li>
97
+ </ol>
98
+ </div></div>
99
+
100
+ <div id="footer">
101
+ Generated on Sun Jun 15 13:02:43 2014 by
102
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
103
+ 0.8.7.4 (ruby-2.1.2).
104
+ </div>
105
+
106
+ </body>
107
+ </html>
data/lib/opt.rb ADDED
@@ -0,0 +1,65 @@
1
+ require 'opt/version'
2
+
3
+ module Opt
4
+ require 'opt/command'
5
+ require 'opt/program'
6
+ require 'opt/option'
7
+ require 'opt/switch'
8
+
9
+ def self.new
10
+ Program.new.tap{|p| yield p if block_given? }
11
+ end
12
+ end
13
+
14
+ # #
15
+ # class Flag < Option
16
+ # attr_reader :negated_long
17
+
18
+ # REGEX = /^(?<word>[^-]+)-(?<rest>.+)/
19
+
20
+ # def initialize(*)
21
+ # super
22
+ # @value = options.fetch(:default, true)
23
+ # @negated_long = Set.new
24
+
25
+ # long.each do |code|
26
+ # if (n = REGEX.match(code)) && (tl = self.class.translations[n[:word]])
27
+ # @negated_long << "#{tl}#{tl.empty? ? '' : '-'}#{n[:rest]}"
28
+ # elsif self.class.translations['']
29
+ # tl = self.class.translations['']
30
+ # @negated_long << "#{tl}#{tl.empty? ? '' : '-'}#{code}"
31
+ # end
32
+ # end
33
+ # end
34
+
35
+ # def accept?(argv)
36
+ # if argv.first.type == :long && negated_long.include?(argv.first.value)
37
+ # return true
38
+ # else
39
+ # super
40
+ # end
41
+ # end
42
+
43
+ # def parse_argv(argv)
44
+ # arg = argv.shift
45
+
46
+ # case arg.type
47
+ # when :long
48
+ # if negated_long.include?(arg.value)
49
+ # !value
50
+ # else
51
+ # value
52
+ # end
53
+ # when :short
54
+ # arg.value.slice!(0, 1)
55
+ # argv.unshift(arg) unless arg.value.empty?
56
+ # value
57
+ # end
58
+ # end
59
+
60
+ # class << self
61
+ # def translations
62
+ # {'with' => 'without', 'without' => 'with', '' => 'no', 'no' => '', 'not' => ''}
63
+ # end
64
+ # end
65
+ # end
@@ -0,0 +1,282 @@
1
+ require 'ostruct'
2
+
3
+ module Opt
4
+ #
5
+ class Command
6
+ #
7
+ # List of registered options for this command.
8
+ #
9
+ # Can be used to add manually created {Option}s but use
10
+ # with care as no collision or sanity checks are done.
11
+ #
12
+ # @return [Array<Option>] Option list.
13
+ #
14
+ attr_reader :options
15
+
16
+ # List of registered subcommands.
17
+ #
18
+ # Can be used to add manually created {Command}s but use
19
+ # with care as no collision or sanity checks are done.
20
+ #
21
+ # @return [Array<Command>] Subcommand list.
22
+ #
23
+ attr_reader :commands
24
+
25
+ # The command name.
26
+ #
27
+ attr_reader :name
28
+
29
+ # @api private
30
+ #
31
+ def initialize(name, opts = {})
32
+ @name = name.to_s.freeze
33
+ @opts = opts
34
+ @options = []
35
+ @commands = []
36
+ end
37
+
38
+ # Register a new option.
39
+ #
40
+ # @example An option named "help" triggered on "--help" or "-h"
41
+ # command.option '--help, -h'
42
+ #
43
+ # @example An option with exactly one argument
44
+ # command.option '--level, -l', nargs: 1
45
+ #
46
+ # @example An option with 2 or more arguments
47
+ # command.option '--files', nargs: [2, :inf]
48
+ # command.option '--files', nargs: [2, :infinity]
49
+ # command.option '--files', nargs: [2, '*']
50
+ #
51
+ # @example An option with 2 to 4 arguments and a specific name
52
+ # command.option '--sum, -s, -a', nargs: (2..4), name: :accumulate
53
+ #
54
+ # @example An option with 0 or more arguments
55
+ # command.option '-x', nargs: '*'
56
+ #
57
+ # @example An option with 1 or more arguments
58
+ # command.option '-x', nargs: '+'
59
+ #
60
+ # @example A free-text option
61
+ # command.option 'file', name: :files, nargs: '*'
62
+ #
63
+ # @param definition [String] The option definition. Usually a command
64
+ # separated list of dashed command line switches. If definition is
65
+ # not dashed a free-text argument will be given. See {Option#initialize}
66
+ # for more information.
67
+ # @param opts [Hash] Option hash passed to {Option#initialize}.
68
+ # Used to specify a name, number of arguments, etc.
69
+ #
70
+ # @raise [ArgumentError] An {ArgumentError} is raised when a colliding
71
+ # option is already registered or you try do define a free-text
72
+ # option while already heaving a subcommand registered.
73
+ #
74
+ # @api public
75
+ # @see Option.new
76
+ #
77
+ def option(definition = nil, opts = {})
78
+ option = Option.new(definition, opts)
79
+
80
+ if commands.any?
81
+ raise ArgumentError.new \
82
+ 'Can only have subcommands OR free-text arguments.'
83
+ end
84
+
85
+ if (opt = options.find{|o| o.collide?(option) })
86
+ raise "Option `#{definition}' collides with already " \
87
+ "registered option: #{opt}"
88
+ else
89
+ options << option
90
+ end
91
+ end
92
+
93
+ # Add a subcommand.
94
+ #
95
+ # A command can either have subcommands or free-text options.
96
+ #
97
+ # @example
98
+ # opt.command 'add' do |cmd|
99
+ # cmd.option '--num, -n'
100
+ # end
101
+ #
102
+ # @param name [String, Symbol, #to_s] The command name. This token
103
+ # will be used to match when parsing arguments.
104
+ # @param opts [Hash] Options.
105
+ #
106
+ # @yield [command] Yield new command.
107
+ # @yieldparam command [Command] The new command.
108
+ #
109
+ # @raise [ArgumentError] An {ArgumentError} will be raised when
110
+ # the command already has a free-text option or if a command
111
+ # with the same name is already registered.
112
+ #
113
+ # @return [Command] The new command.
114
+ #
115
+ # @api public
116
+ # @see Opt::Command#initialize
117
+ #
118
+ def command(name, opts = {})
119
+ if options.any?{|o| o.text? }
120
+ raise ArgumentError.new \
121
+ 'Can only have subcommands OR free-text arguments.'
122
+ end
123
+
124
+ command = Command.new(name, opts)
125
+
126
+ if commands.any?{|c| c.name == command.name }
127
+ raise ArgumentError.new "Command `#{command.name}' already registered."
128
+ end
129
+
130
+ yield command if block_given?
131
+
132
+ commands << command
133
+ command
134
+ end
135
+
136
+ # Return hash with default values for all options.
137
+ #
138
+ # @return [Hash<String, Object>] Hash with option defaults.
139
+ #
140
+ # @api private
141
+ #
142
+ def defaults
143
+ Hash[options.map{|o| [o.name, o.default] }]
144
+ end
145
+
146
+ # Parses given list of command line tokens.
147
+ #
148
+ # @example
149
+ # opt.parse %w(-fd command -x 56 --fuubar)
150
+ #
151
+ # @param argv [Array<String>] List of command line strings.
152
+ # Defaults to {ARGV}.
153
+ #
154
+ # @return [Result] Return a hash-like result object.
155
+ #
156
+ # @api public
157
+ # @see Result
158
+ #
159
+ def parse(argv = ARGV)
160
+ result = Result.new
161
+ result.merge! defaults
162
+
163
+ parse_argv! parse_tokens(argv), result
164
+
165
+ result
166
+ end
167
+
168
+ # @api private
169
+ #
170
+ def parse_argv!(argv, result, options = [])
171
+ options += self.options
172
+
173
+ while argv.any?
174
+ next if options.any?{|o| o.parse!(argv, result) }
175
+
176
+ if argv.first.text?
177
+ if (cmd = commands.find{|c| c.name == argv.first.value })
178
+ result.command << argv.shift.value
179
+ cmd.parse_argv!(argv, result, options)
180
+ next
181
+ end
182
+ end
183
+
184
+ raise "Unknown option (#{argv.first.type}): #{argv.first}"
185
+ end
186
+ end
187
+
188
+ private
189
+
190
+ def parse_tokens(argv)
191
+ tokens = []
192
+ argv.each_with_index do |arg, index|
193
+ if arg == '--'
194
+ return tokens + argv[index + 1..-1].map{|a| Token.new(:text, a) }
195
+ elsif arg[0..1] == '--'
196
+ tokens << Token.new(:long, arg[2..-1])
197
+ elsif arg[0] == '-'
198
+ tokens << Token.new(:short, arg[1..-1])
199
+ else
200
+ tokens << Token.new(:text, arg)
201
+ end
202
+ end
203
+
204
+ tokens
205
+ end
206
+
207
+ # A hash-like result object.
208
+ #
209
+ # Allow for method-access to all key-value pairs similar
210
+ # to `OpenStruct`.
211
+ #
212
+ # @example
213
+ # result = opt.parse %w(--help --level=5 add --exec bash sh)
214
+ # result.help? #=> true
215
+ # result.level #=> "5"
216
+ # result.command #=> ["add"]
217
+ # result.exec #=> ["bash", "sh"]
218
+ #
219
+ class Result < Hash
220
+ #
221
+ # A list of command names.
222
+ #
223
+ # @return [Array<String>] List of commands.
224
+ #
225
+ attr_reader :command
226
+
227
+ # @api private
228
+ #
229
+ def initialize
230
+ @command = []
231
+ super
232
+ end
233
+
234
+ # @api private
235
+ #
236
+ def respond_to_missing?(mth)
237
+ if mth =~ /^(\w)\??$/ && key?($1)
238
+ true
239
+ else
240
+ super
241
+ end
242
+ end
243
+
244
+ # @api private
245
+ #
246
+ def method_missing(mth, *args, &block)
247
+ if mth =~ /^(\w+)\??$/ && key?($1) && args.empty? && block.nil?
248
+ fetch $1
249
+ else
250
+ super
251
+ end
252
+ end
253
+ end
254
+
255
+ # @api private
256
+ #
257
+ Token = Struct.new(:type, :value) do
258
+ def text?
259
+ type == :text
260
+ end
261
+
262
+ def short?
263
+ type == :short
264
+ end
265
+
266
+ def to_s
267
+ case type
268
+ when :long
269
+ "--#{value}"
270
+ when :short
271
+ "-#{value}"
272
+ else
273
+ value
274
+ end
275
+ end
276
+
277
+ def inspect
278
+ "<#{self.class}(#{type}):\"#{self}\">"
279
+ end
280
+ end
281
+ end
282
+ end