opal 1.3.1 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +1 -0
  3. data/.github/workflows/build.yml +20 -21
  4. data/.rubocop.yml +5 -1
  5. data/CHANGELOG.md +119 -11
  6. data/UNRELEASED.md +4 -9
  7. data/benchmark-ips/bm_truthy.rb +30 -0
  8. data/bin/opal-mspec +1 -3
  9. data/bin/opal-repl +1 -2
  10. data/bin/remove-filters +1 -4
  11. data/docs/compiled_ruby.md +37 -22
  12. data/docs/faq.md +1 -1
  13. data/docs/headless_chrome.md +11 -21
  14. data/docs/jquery.md +1 -6
  15. data/docs/opal_parser.md +3 -1
  16. data/docs/promises.md +2 -0
  17. data/docs/releasing.md +3 -0
  18. data/docs/roda-sprockets.md +0 -1
  19. data/docs/source_maps.md +10 -11
  20. data/docs/static_applications.md +2 -2
  21. data/docs/unsupported_features.md +4 -0
  22. data/exe/opal-repl +1 -3
  23. data/lib/opal/ast/builder.rb +1 -1
  24. data/lib/opal/cli.rb +2 -2
  25. data/lib/opal/cli_runners/nodejs.rb +9 -2
  26. data/lib/opal/cli_runners/source-map-support-browser.js +80 -216
  27. data/lib/opal/cli_runners/source-map-support-node.js +80 -216
  28. data/lib/opal/cli_runners/source-map-support.js +5 -1
  29. data/lib/opal/cli_runners/system_runner.rb +10 -4
  30. data/lib/opal/compiler.rb +3 -5
  31. data/lib/opal/fragment.rb +5 -1
  32. data/lib/opal/nodes/args/extract_block_arg.rb +1 -8
  33. data/lib/opal/nodes/args/extract_kwoptarg.rb +1 -3
  34. data/lib/opal/nodes/args/extract_optarg.rb +1 -3
  35. data/lib/opal/nodes/args/extract_post_arg.rb +2 -5
  36. data/lib/opal/nodes/args/extract_post_optarg.rb +2 -7
  37. data/lib/opal/nodes/args/initialize_iterarg.rb +1 -3
  38. data/lib/opal/nodes/args/prepare_post_args.rb +5 -1
  39. data/lib/opal/nodes/base.rb +3 -2
  40. data/lib/opal/nodes/call.rb +20 -9
  41. data/lib/opal/nodes/call_special.rb +50 -0
  42. data/lib/opal/nodes/class.rb +24 -15
  43. data/lib/opal/nodes/constants.rb +23 -5
  44. data/lib/opal/nodes/def.rb +20 -23
  45. data/lib/opal/nodes/defined.rb +5 -5
  46. data/lib/opal/nodes/definitions.rb +2 -2
  47. data/lib/opal/nodes/defs.rb +2 -5
  48. data/lib/opal/nodes/helpers.rb +48 -18
  49. data/lib/opal/nodes/if.rb +113 -8
  50. data/lib/opal/nodes/iter.rb +23 -16
  51. data/lib/opal/nodes/literal.rb +18 -4
  52. data/lib/opal/nodes/logic.rb +2 -1
  53. data/lib/opal/nodes/masgn.rb +4 -9
  54. data/lib/opal/nodes/module.rb +29 -19
  55. data/lib/opal/nodes/node_with_args.rb +1 -7
  56. data/lib/opal/nodes/scope.rb +54 -15
  57. data/lib/opal/nodes/singleton_class.rb +5 -3
  58. data/lib/opal/nodes/super.rb +12 -12
  59. data/lib/opal/nodes/top.rb +34 -31
  60. data/lib/opal/nodes/variables.rb +2 -2
  61. data/lib/opal/nodes/x_string.rb +30 -28
  62. data/lib/opal/nodes.rb +0 -1
  63. data/lib/opal/parser/patch.rb +75 -0
  64. data/lib/opal/parser/with_ruby_lexer.rb +1 -1
  65. data/lib/opal/regexp_anchors.rb +7 -7
  66. data/lib/opal/requires.rb +19 -0
  67. data/lib/opal/rewriters/pattern_matching.rb +1 -1
  68. data/lib/opal/rewriters/returnable_logic.rb +102 -4
  69. data/lib/opal/util.rb +2 -2
  70. data/lib/opal/version.rb +1 -1
  71. data/lib/opal.rb +1 -17
  72. data/opal/corelib/array/pack.rb +11 -11
  73. data/opal/corelib/array.rb +193 -152
  74. data/opal/corelib/basic_object.rb +19 -15
  75. data/opal/corelib/binding.rb +7 -7
  76. data/opal/corelib/boolean.rb +12 -15
  77. data/opal/corelib/class.rb +23 -1
  78. data/opal/corelib/comparable.rb +8 -8
  79. data/opal/corelib/complex/base.rb +2 -2
  80. data/opal/corelib/complex.rb +79 -88
  81. data/opal/corelib/constants.rb +9 -9
  82. data/opal/corelib/dir.rb +4 -3
  83. data/opal/corelib/enumerable.rb +140 -127
  84. data/opal/corelib/enumerator/arithmetic_sequence.rb +177 -0
  85. data/opal/corelib/enumerator/chain.rb +42 -0
  86. data/opal/corelib/enumerator/generator.rb +35 -0
  87. data/opal/corelib/enumerator/lazy.rb +243 -0
  88. data/opal/corelib/enumerator/yielder.rb +36 -0
  89. data/opal/corelib/enumerator.rb +45 -300
  90. data/opal/corelib/error/errno.rb +47 -0
  91. data/opal/corelib/error.rb +62 -60
  92. data/opal/corelib/file.rb +26 -12
  93. data/opal/corelib/hash.rb +98 -107
  94. data/opal/corelib/helpers.rb +62 -13
  95. data/opal/corelib/io.rb +48 -35
  96. data/opal/corelib/kernel/format.rb +29 -29
  97. data/opal/corelib/kernel.rb +86 -83
  98. data/opal/corelib/main.rb +14 -12
  99. data/opal/corelib/marshal/read_buffer.rb +15 -15
  100. data/opal/corelib/marshal/write_buffer.rb +45 -44
  101. data/opal/corelib/marshal.rb +3 -3
  102. data/opal/corelib/math.rb +50 -50
  103. data/opal/corelib/method.rb +12 -8
  104. data/opal/corelib/module.rb +79 -75
  105. data/opal/corelib/nil.rb +9 -11
  106. data/opal/corelib/number.rb +113 -118
  107. data/opal/corelib/numeric.rb +37 -33
  108. data/opal/corelib/object_space.rb +11 -10
  109. data/opal/corelib/pack_unpack/format_string_parser.rb +3 -3
  110. data/opal/corelib/pattern_matching/base.rb +7 -7
  111. data/opal/corelib/pattern_matching.rb +1 -1
  112. data/opal/corelib/proc.rb +15 -16
  113. data/opal/corelib/process/base.rb +2 -2
  114. data/opal/corelib/process/status.rb +21 -0
  115. data/opal/corelib/process.rb +5 -5
  116. data/opal/corelib/random/formatter.rb +11 -11
  117. data/opal/corelib/random/math_random.js.rb +1 -1
  118. data/opal/corelib/random/mersenne_twister.rb +3 -3
  119. data/opal/corelib/random/seedrandom.js.rb +3 -3
  120. data/opal/corelib/random.rb +17 -17
  121. data/opal/corelib/range.rb +51 -35
  122. data/opal/corelib/rational/base.rb +4 -4
  123. data/opal/corelib/rational.rb +61 -62
  124. data/opal/corelib/regexp.rb +54 -45
  125. data/opal/corelib/runtime.js +247 -141
  126. data/opal/corelib/string/encoding.rb +21 -21
  127. data/opal/corelib/string/unpack.rb +19 -14
  128. data/opal/corelib/string.rb +137 -130
  129. data/opal/corelib/struct.rb +59 -46
  130. data/opal/corelib/time.rb +47 -57
  131. data/opal/corelib/trace_point.rb +2 -2
  132. data/opal/corelib/unsupported.rb +31 -120
  133. data/opal/corelib/variables.rb +3 -3
  134. data/opal/opal/base.rb +9 -8
  135. data/opal/opal/full.rb +8 -8
  136. data/opal/opal/mini.rb +17 -17
  137. data/opal/opal.rb +17 -18
  138. data/opal.gemspec +1 -1
  139. data/spec/filters/bugs/array.rb +4 -24
  140. data/spec/filters/bugs/basicobject.rb +0 -1
  141. data/spec/filters/bugs/bigdecimal.rb +0 -23
  142. data/spec/filters/bugs/binding.rb +0 -1
  143. data/spec/filters/bugs/boolean.rb +3 -0
  144. data/spec/filters/bugs/class.rb +2 -0
  145. data/spec/filters/bugs/date.rb +0 -5
  146. data/spec/filters/bugs/encoding.rb +8 -50
  147. data/spec/filters/bugs/enumerable.rb +4 -1
  148. data/spec/filters/bugs/enumerator.rb +3 -36
  149. data/spec/filters/bugs/exception.rb +0 -2
  150. data/spec/filters/bugs/file.rb +0 -2
  151. data/spec/filters/bugs/float.rb +0 -3
  152. data/spec/filters/bugs/hash.rb +5 -3
  153. data/spec/filters/bugs/integer.rb +2 -3
  154. data/spec/filters/bugs/kernel.rb +2 -31
  155. data/spec/filters/bugs/language.rb +29 -49
  156. data/spec/filters/bugs/main.rb +0 -2
  157. data/spec/filters/bugs/marshal.rb +2 -3
  158. data/spec/filters/bugs/matrix.rb +0 -36
  159. data/spec/filters/bugs/module.rb +7 -61
  160. data/spec/filters/bugs/numeric.rb +0 -7
  161. data/spec/filters/bugs/objectspace.rb +1 -1
  162. data/spec/filters/bugs/pack_unpack.rb +0 -4
  163. data/spec/filters/bugs/proc.rb +0 -9
  164. data/spec/filters/bugs/random.rb +0 -5
  165. data/spec/filters/bugs/range.rb +1 -6
  166. data/spec/filters/bugs/regexp.rb +0 -3
  167. data/spec/filters/bugs/set.rb +8 -1
  168. data/spec/filters/bugs/string.rb +9 -34
  169. data/spec/filters/bugs/stringscanner.rb +8 -7
  170. data/spec/filters/bugs/struct.rb +2 -3
  171. data/spec/filters/bugs/symbol.rb +0 -1
  172. data/spec/filters/bugs/time.rb +0 -8
  173. data/spec/filters/bugs/unboundmethod.rb +0 -8
  174. data/spec/filters/bugs/warnings.rb +1 -7
  175. data/spec/filters/unsupported/freeze.rb +24 -0
  176. data/spec/filters/unsupported/integer.rb +1 -0
  177. data/spec/filters/unsupported/kernel.rb +12 -0
  178. data/spec/filters/unsupported/privacy.rb +3 -0
  179. data/spec/filters/unsupported/string.rb +2 -0
  180. data/spec/lib/builder_spec.rb +2 -2
  181. data/spec/lib/cli_spec.rb +1 -1
  182. data/spec/lib/compiler_spec.rb +37 -37
  183. data/spec/lib/simple_server_spec.rb +2 -2
  184. data/spec/lib/source_map/file_spec.rb +1 -1
  185. data/spec/opal/compiler/irb_spec.rb +2 -2
  186. data/spec/opal/core/io/read_spec.rb +69 -0
  187. data/spec/opal/core/kernel/puts_spec.rb +90 -0
  188. data/spec/opal/core/language/super_spec.rb +21 -0
  189. data/spec/opal/core/language/xstring_spec.rb +13 -0
  190. data/spec/opal/core/language_spec.rb +14 -0
  191. data/spec/opal/core/string/gsub_spec.rb +8 -0
  192. data/spec/ruby_specs +4 -2
  193. data/spec/support/rewriters_helper.rb +1 -1
  194. data/stdlib/bigdecimal.rb +7 -11
  195. data/stdlib/buffer/view.rb +2 -2
  196. data/stdlib/buffer.rb +2 -2
  197. data/stdlib/date.rb +5 -6
  198. data/stdlib/erb.rb +1 -0
  199. data/stdlib/js.rb +2 -1
  200. data/stdlib/native.rb +7 -8
  201. data/stdlib/nodejs/argf.rb +4 -4
  202. data/stdlib/nodejs/base.rb +29 -0
  203. data/stdlib/nodejs/dir.rb +1 -1
  204. data/stdlib/nodejs/env.rb +6 -9
  205. data/stdlib/nodejs/file.rb +23 -17
  206. data/stdlib/nodejs/fileutils.rb +3 -3
  207. data/stdlib/nodejs/io.rb +2 -20
  208. data/stdlib/nodejs/irb.rb +0 -0
  209. data/stdlib/nodejs/kernel.rb +2 -37
  210. data/stdlib/nodejs.rb +1 -3
  211. data/stdlib/opal/miniracer.rb +2 -0
  212. data/stdlib/opal/platform.rb +6 -13
  213. data/stdlib/opal/replutils.rb +16 -5
  214. data/stdlib/opal-parser.rb +2 -2
  215. data/stdlib/optparse/ac.rb +54 -0
  216. data/stdlib/optparse/date.rb +14 -0
  217. data/stdlib/optparse/kwargs.rb +22 -0
  218. data/stdlib/optparse/shellwords.rb +7 -0
  219. data/stdlib/optparse/time.rb +15 -0
  220. data/stdlib/optparse/uri.rb +7 -0
  221. data/stdlib/optparse/version.rb +69 -0
  222. data/stdlib/optparse.rb +2279 -0
  223. data/stdlib/pathname.rb +5 -6
  224. data/stdlib/pp.rb +18 -2
  225. data/stdlib/promise/v2.rb +18 -29
  226. data/stdlib/promise.rb +15 -21
  227. data/stdlib/quickjs/io.rb +0 -2
  228. data/stdlib/quickjs/kernel.rb +0 -2
  229. data/stdlib/quickjs.rb +2 -0
  230. data/stdlib/set.rb +32 -32
  231. data/stdlib/shellwords.rb +240 -0
  232. data/stdlib/stringio.rb +3 -6
  233. data/stdlib/strscan.rb +5 -8
  234. data/stdlib/template.rb +2 -2
  235. data/stdlib/thread.rb +7 -9
  236. data/tasks/linting-parse-eslint-results.js +1 -0
  237. data/tasks/linting.rake +0 -10
  238. data/tasks/performance.rake +5 -2
  239. data/tasks/testing/mspec_special_calls.rb +0 -12
  240. data/tasks/testing.rake +55 -37
  241. data/test/nodejs/test_file.rb +11 -0
  242. metadata +55 -8
  243. data/lib/opal/nodes/case.rb +0 -114
@@ -0,0 +1,2279 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # optparse.rb - command-line option analysis with the OptionParser class.
4
+ #
5
+ # Author:: Nobu Nakada
6
+ # Documentation:: Nobu Nakada and Gavin Sinclair.
7
+ #
8
+ # See OptionParser for documentation.
9
+ #
10
+
11
+
12
+ #--
13
+ # == Developer Documentation (not for RDoc output)
14
+ #
15
+ # === Class tree
16
+ #
17
+ # - OptionParser:: front end
18
+ # - OptionParser::Switch:: each switches
19
+ # - OptionParser::List:: options list
20
+ # - OptionParser::ParseError:: errors on parsing
21
+ # - OptionParser::AmbiguousOption
22
+ # - OptionParser::NeedlessArgument
23
+ # - OptionParser::MissingArgument
24
+ # - OptionParser::InvalidOption
25
+ # - OptionParser::InvalidArgument
26
+ # - OptionParser::AmbiguousArgument
27
+ #
28
+ # === Object relationship diagram
29
+ #
30
+ # +--------------+
31
+ # | OptionParser |<>-----+
32
+ # +--------------+ | +--------+
33
+ # | ,-| Switch |
34
+ # on_head -------->+---------------+ / +--------+
35
+ # accept/reject -->| List |<|>-
36
+ # | |<|>- +----------+
37
+ # on ------------->+---------------+ `-| argument |
38
+ # : : | class |
39
+ # +---------------+ |==========|
40
+ # on_tail -------->| | |pattern |
41
+ # +---------------+ |----------|
42
+ # OptionParser.accept ->| DefaultList | |converter |
43
+ # reject |(shared between| +----------+
44
+ # | all instances)|
45
+ # +---------------+
46
+ #
47
+ #++
48
+ #
49
+ # == OptionParser
50
+ #
51
+ # === New to \OptionParser?
52
+ #
53
+ # See the {Tutorial}[./doc/optparse/tutorial_rdoc.html].
54
+ #
55
+ # === Introduction
56
+ #
57
+ # OptionParser is a class for command-line option analysis. It is much more
58
+ # advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented
59
+ # solution.
60
+ #
61
+ # === Features
62
+ #
63
+ # 1. The argument specification and the code to handle it are written in the
64
+ # same place.
65
+ # 2. It can output an option summary; you don't need to maintain this string
66
+ # separately.
67
+ # 3. Optional and mandatory arguments are specified very gracefully.
68
+ # 4. Arguments can be automatically converted to a specified class.
69
+ # 5. Arguments can be restricted to a certain set.
70
+ #
71
+ # All of these features are demonstrated in the examples below. See
72
+ # #make_switch for full documentation.
73
+ #
74
+ # === Minimal example
75
+ #
76
+ # require 'optparse'
77
+ #
78
+ # options = {}
79
+ # OptionParser.new do |parser|
80
+ # parser.banner = "Usage: example.rb [options]"
81
+ #
82
+ # parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
83
+ # options[:verbose] = v
84
+ # end
85
+ # end.parse!
86
+ #
87
+ # p options
88
+ # p ARGV
89
+ #
90
+ # === Generating Help
91
+ #
92
+ # OptionParser can be used to automatically generate help for the commands you
93
+ # write:
94
+ #
95
+ # require 'optparse'
96
+ #
97
+ # Options = Struct.new(:name)
98
+ #
99
+ # class Parser
100
+ # def self.parse(options)
101
+ # args = Options.new("world")
102
+ #
103
+ # opt_parser = OptionParser.new do |parser|
104
+ # parser.banner = "Usage: example.rb [options]"
105
+ #
106
+ # parser.on("-nNAME", "--name=NAME", "Name to say hello to") do |n|
107
+ # args.name = n
108
+ # end
109
+ #
110
+ # parser.on("-h", "--help", "Prints this help") do
111
+ # puts parser
112
+ # exit
113
+ # end
114
+ # end
115
+ #
116
+ # opt_parser.parse!(options)
117
+ # return args
118
+ # end
119
+ # end
120
+ # options = Parser.parse %w[--help]
121
+ #
122
+ # #=>
123
+ # # Usage: example.rb [options]
124
+ # # -n, --name=NAME Name to say hello to
125
+ # # -h, --help Prints this help
126
+ #
127
+ # === Required Arguments
128
+ #
129
+ # For options that require an argument, option specification strings may include an
130
+ # option name in all caps. If an option is used without the required argument,
131
+ # an exception will be raised.
132
+ #
133
+ # require 'optparse'
134
+ #
135
+ # options = {}
136
+ # OptionParser.new do |parser|
137
+ # parser.on("-r", "--require LIBRARY",
138
+ # "Require the LIBRARY before executing your script") do |lib|
139
+ # puts "You required #{lib}!"
140
+ # end
141
+ # end.parse!
142
+ #
143
+ # Used:
144
+ #
145
+ # $ ruby optparse-test.rb -r
146
+ # optparse-test.rb:9:in `<main>': missing argument: -r (OptionParser::MissingArgument)
147
+ # $ ruby optparse-test.rb -r my-library
148
+ # You required my-library!
149
+ #
150
+ # === Type Coercion
151
+ #
152
+ # OptionParser supports the ability to coerce command line arguments
153
+ # into objects for us.
154
+ #
155
+ # OptionParser comes with a few ready-to-use kinds of type
156
+ # coercion. They are:
157
+ #
158
+ # - Date -- Anything accepted by +Date.parse+
159
+ # - DateTime -- Anything accepted by +DateTime.parse+
160
+ # - Time -- Anything accepted by +Time.httpdate+ or +Time.parse+
161
+ # - URI -- Anything accepted by +URI.parse+
162
+ # - Shellwords -- Anything accepted by +Shellwords.shellwords+
163
+ # - String -- Any non-empty string
164
+ # - Integer -- Any integer. Will convert octal. (e.g. 124, -3, 040)
165
+ # - Float -- Any float. (e.g. 10, 3.14, -100E+13)
166
+ # - Numeric -- Any integer, float, or rational (1, 3.4, 1/3)
167
+ # - DecimalInteger -- Like +Integer+, but no octal format.
168
+ # - OctalInteger -- Like +Integer+, but no decimal format.
169
+ # - DecimalNumeric -- Decimal integer or float.
170
+ # - TrueClass -- Accepts '+, yes, true, -, no, false' and
171
+ # defaults as +true+
172
+ # - FalseClass -- Same as +TrueClass+, but defaults to +false+
173
+ # - Array -- Strings separated by ',' (e.g. 1,2,3)
174
+ # - Regexp -- Regular expressions. Also includes options.
175
+ #
176
+ # We can also add our own coercions, which we will cover below.
177
+ #
178
+ # ==== Using Built-in Conversions
179
+ #
180
+ # As an example, the built-in +Time+ conversion is used. The other built-in
181
+ # conversions behave in the same way.
182
+ # OptionParser will attempt to parse the argument
183
+ # as a +Time+. If it succeeds, that time will be passed to the
184
+ # handler block. Otherwise, an exception will be raised.
185
+ #
186
+ # require 'optparse'
187
+ # require 'optparse/time'
188
+ # OptionParser.new do |parser|
189
+ # parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
190
+ # p time
191
+ # end
192
+ # end.parse!
193
+ #
194
+ # Used:
195
+ #
196
+ # $ ruby optparse-test.rb -t nonsense
197
+ # ... invalid argument: -t nonsense (OptionParser::InvalidArgument)
198
+ # $ ruby optparse-test.rb -t 10-11-12
199
+ # 2010-11-12 00:00:00 -0500
200
+ # $ ruby optparse-test.rb -t 9:30
201
+ # 2014-08-13 09:30:00 -0400
202
+ #
203
+ # ==== Creating Custom Conversions
204
+ #
205
+ # The +accept+ method on OptionParser may be used to create converters.
206
+ # It specifies which conversion block to call whenever a class is specified.
207
+ # The example below uses it to fetch a +User+ object before the +on+ handler receives it.
208
+ #
209
+ # require 'optparse'
210
+ #
211
+ # User = Struct.new(:id, :name)
212
+ #
213
+ # def find_user id
214
+ # not_found = ->{ raise "No User Found for id #{id}" }
215
+ # [ User.new(1, "Sam"),
216
+ # User.new(2, "Gandalf") ].find(not_found) do |u|
217
+ # u.id == id
218
+ # end
219
+ # end
220
+ #
221
+ # op = OptionParser.new
222
+ # op.accept(User) do |user_id|
223
+ # find_user user_id.to_i
224
+ # end
225
+ #
226
+ # op.on("--user ID", User) do |user|
227
+ # puts user
228
+ # end
229
+ #
230
+ # op.parse!
231
+ #
232
+ # Used:
233
+ #
234
+ # $ ruby optparse-test.rb --user 1
235
+ # #<struct User id=1, name="Sam">
236
+ # $ ruby optparse-test.rb --user 2
237
+ # #<struct User id=2, name="Gandalf">
238
+ # $ ruby optparse-test.rb --user 3
239
+ # optparse-test.rb:15:in `block in find_user': No User Found for id 3 (RuntimeError)
240
+ #
241
+ # === Store options to a Hash
242
+ #
243
+ # The +into+ option of +order+, +parse+ and so on methods stores command line options into a Hash.
244
+ #
245
+ # require 'optparse'
246
+ #
247
+ # options = {}
248
+ # OptionParser.new do |parser|
249
+ # parser.on('-a')
250
+ # parser.on('-b NUM', Integer)
251
+ # parser.on('-v', '--verbose')
252
+ # end.parse!(into: options)
253
+ #
254
+ # p options
255
+ #
256
+ # Used:
257
+ #
258
+ # $ ruby optparse-test.rb -a
259
+ # {:a=>true}
260
+ # $ ruby optparse-test.rb -a -v
261
+ # {:a=>true, :verbose=>true}
262
+ # $ ruby optparse-test.rb -a -b 100
263
+ # {:a=>true, :b=>100}
264
+ #
265
+ # === Complete example
266
+ #
267
+ # The following example is a complete Ruby program. You can run it and see the
268
+ # effect of specifying various options. This is probably the best way to learn
269
+ # the features of +optparse+.
270
+ #
271
+ # require 'optparse'
272
+ # require 'optparse/time'
273
+ # require 'ostruct'
274
+ # require 'pp'
275
+ #
276
+ # class OptparseExample
277
+ # Version = '1.0.0'
278
+ #
279
+ # CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
280
+ # CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
281
+ #
282
+ # class ScriptOptions
283
+ # attr_accessor :library, :inplace, :encoding, :transfer_type,
284
+ # :verbose, :extension, :delay, :time, :record_separator,
285
+ # :list
286
+ #
287
+ # def initialize
288
+ # self.library = []
289
+ # self.inplace = false
290
+ # self.encoding = "utf8"
291
+ # self.transfer_type = :auto
292
+ # self.verbose = false
293
+ # end
294
+ #
295
+ # def define_options(parser)
296
+ # parser.banner = "Usage: example.rb [options]"
297
+ # parser.separator ""
298
+ # parser.separator "Specific options:"
299
+ #
300
+ # # add additional options
301
+ # perform_inplace_option(parser)
302
+ # delay_execution_option(parser)
303
+ # execute_at_time_option(parser)
304
+ # specify_record_separator_option(parser)
305
+ # list_example_option(parser)
306
+ # specify_encoding_option(parser)
307
+ # optional_option_argument_with_keyword_completion_option(parser)
308
+ # boolean_verbose_option(parser)
309
+ #
310
+ # parser.separator ""
311
+ # parser.separator "Common options:"
312
+ # # No argument, shows at tail. This will print an options summary.
313
+ # # Try it and see!
314
+ # parser.on_tail("-h", "--help", "Show this message") do
315
+ # puts parser
316
+ # exit
317
+ # end
318
+ # # Another typical switch to print the version.
319
+ # parser.on_tail("--version", "Show version") do
320
+ # puts Version
321
+ # exit
322
+ # end
323
+ # end
324
+ #
325
+ # def perform_inplace_option(parser)
326
+ # # Specifies an optional option argument
327
+ # parser.on("-i", "--inplace [EXTENSION]",
328
+ # "Edit ARGV files in place",
329
+ # "(make backup if EXTENSION supplied)") do |ext|
330
+ # self.inplace = true
331
+ # self.extension = ext || ''
332
+ # self.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot.
333
+ # end
334
+ # end
335
+ #
336
+ # def delay_execution_option(parser)
337
+ # # Cast 'delay' argument to a Float.
338
+ # parser.on("--delay N", Float, "Delay N seconds before executing") do |n|
339
+ # self.delay = n
340
+ # end
341
+ # end
342
+ #
343
+ # def execute_at_time_option(parser)
344
+ # # Cast 'time' argument to a Time object.
345
+ # parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
346
+ # self.time = time
347
+ # end
348
+ # end
349
+ #
350
+ # def specify_record_separator_option(parser)
351
+ # # Cast to octal integer.
352
+ # parser.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger,
353
+ # "Specify record separator (default \\0)") do |rs|
354
+ # self.record_separator = rs
355
+ # end
356
+ # end
357
+ #
358
+ # def list_example_option(parser)
359
+ # # List of arguments.
360
+ # parser.on("--list x,y,z", Array, "Example 'list' of arguments") do |list|
361
+ # self.list = list
362
+ # end
363
+ # end
364
+ #
365
+ # def specify_encoding_option(parser)
366
+ # # Keyword completion. We are specifying a specific set of arguments (CODES
367
+ # # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
368
+ # # the shortest unambiguous text.
369
+ # code_list = (CODE_ALIASES.keys + CODES).join(', ')
370
+ # parser.on("--code CODE", CODES, CODE_ALIASES, "Select encoding",
371
+ # "(#{code_list})") do |encoding|
372
+ # self.encoding = encoding
373
+ # end
374
+ # end
375
+ #
376
+ # def optional_option_argument_with_keyword_completion_option(parser)
377
+ # # Optional '--type' option argument with keyword completion.
378
+ # parser.on("--type [TYPE]", [:text, :binary, :auto],
379
+ # "Select transfer type (text, binary, auto)") do |t|
380
+ # self.transfer_type = t
381
+ # end
382
+ # end
383
+ #
384
+ # def boolean_verbose_option(parser)
385
+ # # Boolean switch.
386
+ # parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
387
+ # self.verbose = v
388
+ # end
389
+ # end
390
+ # end
391
+ #
392
+ # #
393
+ # # Return a structure describing the options.
394
+ # #
395
+ # def parse(args)
396
+ # # The options specified on the command line will be collected in
397
+ # # *options*.
398
+ #
399
+ # @options = ScriptOptions.new
400
+ # @args = OptionParser.new do |parser|
401
+ # @options.define_options(parser)
402
+ # parser.parse!(args)
403
+ # end
404
+ # @options
405
+ # end
406
+ #
407
+ # attr_reader :parser, :options
408
+ # end # class OptparseExample
409
+ #
410
+ # example = OptparseExample.new
411
+ # options = example.parse(ARGV)
412
+ # pp options # example.options
413
+ # pp ARGV
414
+ #
415
+ # === Shell Completion
416
+ #
417
+ # For modern shells (e.g. bash, zsh, etc.), you can use shell
418
+ # completion for command line options.
419
+ #
420
+ # === Further documentation
421
+ #
422
+ # The above examples, along with the accompanying
423
+ # {Tutorial}[./doc/optparse/tutorial_rdoc.html],
424
+ # should be enough to learn how to use this class.
425
+ # If you have any questions, file a ticket at http://bugs.ruby-lang.org.
426
+ #
427
+ class OptionParser
428
+ OptionParser::Version = '0.1.1'
429
+
430
+ # :stopdoc:
431
+ NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
432
+ RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze
433
+ OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze
434
+ # :startdoc:
435
+
436
+ #
437
+ # Keyword completion module. This allows partial arguments to be specified
438
+ # and resolved against a list of acceptable values.
439
+ #
440
+ module Completion
441
+ def self.regexp(key, icase)
442
+ Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'), icase)
443
+ end
444
+
445
+ def self.candidate(key, icase = false, pat = nil, &block)
446
+ pat ||= Completion.regexp(key, icase)
447
+ candidates = []
448
+ block.call do |k, *v|
449
+ (if Regexp === k
450
+ kn = ''
451
+ k === key
452
+ else
453
+ kn = defined?(k.id2name) ? k.id2name : k
454
+ pat === kn
455
+ end) || next
456
+ v << k if v.empty?
457
+ candidates << [k, v, kn]
458
+ end
459
+ candidates
460
+ end
461
+
462
+ def candidate(key, icase = false, pat = nil)
463
+ Completion.candidate(key, icase, pat, &method(:each))
464
+ end
465
+
466
+ public
467
+
468
+ def complete(key, icase = false, pat = nil)
469
+ candidates = candidate(key, icase, pat, &method(:each)).sort_by { |k, v, kn| kn.size }
470
+ if candidates.size == 1
471
+ canon, sw, * = candidates[0]
472
+ elsif candidates.size > 1
473
+ canon, sw, cn = candidates.shift
474
+ candidates.each do |k, v, kn|
475
+ next if sw == v
476
+ if (String === cn) && (String === kn)
477
+ if cn.rindex(kn, 0)
478
+ canon, sw, cn = k, v, kn
479
+ next
480
+ elsif kn.rindex(cn, 0)
481
+ next
482
+ end
483
+ end
484
+ throw :ambiguous, key
485
+ end
486
+ end
487
+ if canon
488
+ block_given? || (return key, *sw)
489
+ yield(key, *sw)
490
+ end
491
+ end
492
+
493
+ def convert(opt = nil, val = nil, *)
494
+ val
495
+ end
496
+ end
497
+
498
+
499
+ #
500
+ # Map from option/keyword string to object with completion.
501
+ #
502
+ class OptionMap < Hash
503
+ include Completion
504
+ end
505
+
506
+
507
+ #
508
+ # Individual switch class. Not important to the user.
509
+ #
510
+ # Defined within Switch are several Switch-derived classes: NoArgument,
511
+ # RequiredArgument, etc.
512
+ #
513
+ class Switch
514
+ attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block
515
+
516
+ #
517
+ # Guesses argument style from +arg+. Returns corresponding
518
+ # OptionParser::Switch class (OptionalArgument, etc.).
519
+ #
520
+ def self.guess(arg)
521
+ case arg
522
+ when ''
523
+ t = self
524
+ when /\A=?\[/
525
+ t = Switch::OptionalArgument
526
+ when /\A\s+\[/
527
+ t = Switch::PlacedArgument
528
+ else
529
+ t = Switch::RequiredArgument
530
+ end
531
+ (self >= t) || incompatible_argument_styles(arg, t)
532
+ t
533
+ end
534
+
535
+ def self.incompatible_argument_styles(arg, t)
536
+ raise(ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}",
537
+ ParseError.filter_backtrace(caller(2))
538
+ )
539
+ end
540
+
541
+ def self.pattern
542
+ NilClass
543
+ end
544
+
545
+ def initialize(pattern = nil, conv = nil,
546
+ short = nil, long = nil, arg = nil,
547
+ desc = ([] if short || long), block = nil, &_block)
548
+ raise if Array === pattern
549
+ block ||= _block
550
+ @pattern, @conv, @short, @long, @arg, @desc, @block =
551
+ pattern, conv, short, long, arg, desc, block
552
+ end
553
+
554
+ #
555
+ # Parses +arg+ and returns rest of +arg+ and matched portion to the
556
+ # argument pattern. Yields when the pattern doesn't match substring.
557
+ #
558
+ def parse_arg(arg) # :nodoc:
559
+ pattern || (return nil, [arg])
560
+ unless m = pattern.match(arg)
561
+ yield(InvalidArgument, arg)
562
+ return arg, []
563
+ end
564
+ if String === m
565
+ m = [s = m]
566
+ else
567
+ m = m.to_a
568
+ s = m[0]
569
+ return nil, m unless String === s
570
+ end
571
+ raise InvalidArgument, arg unless arg.rindex(s, 0)
572
+ return nil, m if s.length == arg.length
573
+ yield(InvalidArgument, arg) # didn't match whole arg
574
+ [arg[s.length..-1], m]
575
+ end
576
+ private :parse_arg
577
+
578
+ #
579
+ # Parses argument, converts and returns +arg+, +block+ and result of
580
+ # conversion. Yields at semi-error condition instead of raising an
581
+ # exception.
582
+ #
583
+ def conv_arg(arg, val = []) # :nodoc:
584
+ if conv
585
+ val = conv.call(*val)
586
+ else
587
+ val = proc { |v| v }.call(*val)
588
+ end
589
+ [arg, block, val]
590
+ end
591
+ private :conv_arg
592
+
593
+ #
594
+ # Produces the summary text. Each line of the summary is yielded to the
595
+ # block (without newline).
596
+ #
597
+ # +sdone+:: Already summarized short style options keyed hash.
598
+ # +ldone+:: Already summarized long style options keyed hash.
599
+ # +width+:: Width of left side (option part). In other words, the right
600
+ # side (description part) starts after +width+ columns.
601
+ # +max+:: Maximum width of left side -> the options are filled within
602
+ # +max+ columns.
603
+ # +indent+:: Prefix string indents all summarized lines.
604
+ #
605
+ def summarize(sdone = {}, ldone = {}, width = 1, max = width - 1, indent = '')
606
+ sopts, lopts = [], [], nil
607
+ @short.each { |s| sdone.fetch(s) { sopts << s }; sdone[s] = true } if @short
608
+ @long.each { |s| ldone.fetch(s) { lopts << s }; ldone[s] = true } if @long
609
+ return if sopts.empty? && lopts.empty? # completely hidden
610
+
611
+ left = [sopts.join(', ')]
612
+ right = desc.dup
613
+
614
+ while s = lopts.shift
615
+ l = left[-1].length + s.length
616
+ l += arg.length if left.size == 1 && arg
617
+ (l < max) || sopts.empty? || left << +''
618
+ left[-1] += (left[-1].empty? ? ' ' * 4 : ', ') + s
619
+ end
620
+
621
+ if arg
622
+ left[0] += (left[1] ? arg.sub(/\A(\[?)=/, '\1') + ',' : arg)
623
+ end
624
+ mlen = left.collect(&:length).max.to_i
625
+ while (mlen > width) && (l = left.shift)
626
+ mlen = left.collect(&:length).max.to_i if l.length == mlen
627
+ if (l.length < width) && (r = right[0]) && !r.empty?
628
+ l = l.to_s.ljust(width) + ' ' + r
629
+ right.shift
630
+ end
631
+ yield(indent + l)
632
+ end
633
+
634
+ while begin l = left.shift; r = right.shift; l || r end
635
+ l = l.to_s.ljust(width) + ' ' + r if r && !r.empty?
636
+ yield(indent + l)
637
+ end
638
+
639
+ self
640
+ end
641
+
642
+ def add_banner(to) # :nodoc:
643
+ unless @short || @long
644
+ s = desc.join
645
+ to << ' [' + s + ']...' unless s.empty?
646
+ end
647
+ to
648
+ end
649
+
650
+ def match_nonswitch?(str) # :nodoc:
651
+ @pattern =~ str unless @short || @long
652
+ end
653
+
654
+ #
655
+ # Main name of the switch.
656
+ #
657
+ def switch_name
658
+ (long.first || short.first).sub(/\A-+(?:\[no-\])?/, '')
659
+ end
660
+
661
+ def compsys(sdone, ldone) # :nodoc:
662
+ sopts, lopts = [], []
663
+ @short.each { |s| sdone.fetch(s) { sopts << s }; sdone[s] = true } if @short
664
+ @long.each { |s| ldone.fetch(s) { lopts << s }; ldone[s] = true } if @long
665
+ return if sopts.empty? && lopts.empty? # completely hidden
666
+
667
+ (sopts + lopts).each do |opt|
668
+ # "(-x -c -r)-l[left justify]"
669
+ if /^--\[no-\](.+)$/ =~ opt
670
+ o = Regexp.last_match(1)
671
+ yield("--#{o}", desc.join(''))
672
+ yield("--no-#{o}", desc.join(''))
673
+ else
674
+ yield(opt.to_s, desc.join(''))
675
+ end
676
+ end
677
+ end
678
+
679
+ #
680
+ # Switch that takes no arguments.
681
+ #
682
+ class NoArgument < self
683
+ #
684
+ # Raises an exception if any arguments given.
685
+ #
686
+ def parse(arg, argv)
687
+ yield(NeedlessArgument, arg) if arg
688
+ conv_arg(arg)
689
+ end
690
+
691
+ def self.incompatible_argument_styles(*)
692
+ end
693
+
694
+ def self.pattern
695
+ Object
696
+ end
697
+ end
698
+
699
+ #
700
+ # Switch that takes an argument.
701
+ #
702
+ class RequiredArgument < self
703
+ #
704
+ # Raises an exception if argument is not present.
705
+ #
706
+ def parse(arg, argv)
707
+ unless arg
708
+ raise MissingArgument if argv.empty?
709
+ arg = argv.shift
710
+ end
711
+ conv_arg(*parse_arg(arg, &method(:raise)))
712
+ end
713
+ end
714
+
715
+ #
716
+ # Switch that can omit argument.
717
+ #
718
+ class OptionalArgument < self
719
+ #
720
+ # Parses argument if given, or uses default value.
721
+ #
722
+ def parse(arg, argv, &error)
723
+ if arg
724
+ conv_arg(*parse_arg(arg, &error))
725
+ else
726
+ conv_arg(arg)
727
+ end
728
+ end
729
+ end
730
+
731
+ #
732
+ # Switch that takes an argument, which does not begin with '-'.
733
+ #
734
+ class PlacedArgument < self
735
+ #
736
+ # Returns nil if argument is not present or begins with '-'.
737
+ #
738
+ def parse(arg, argv, &error)
739
+ if !(val = arg) && (argv.empty? || /\A-/ =~ (val = argv[0]))
740
+ return nil, block, nil
741
+ end
742
+ opt = (val = parse_arg(val, &error))[1]
743
+ val = conv_arg(*val)
744
+ if opt && !arg
745
+ argv.shift
746
+ else
747
+ val[0] = nil
748
+ end
749
+ val
750
+ end
751
+ end
752
+ end
753
+
754
+ #
755
+ # Simple option list providing mapping from short and/or long option
756
+ # string to OptionParser::Switch and mapping from acceptable argument to
757
+ # matching pattern and converter pair. Also provides summary feature.
758
+ #
759
+ class List
760
+ # Map from acceptable argument types to pattern and converter pairs.
761
+ attr_reader :atype
762
+
763
+ # Map from short style option switches to actual switch objects.
764
+ attr_reader :short
765
+
766
+ # Map from long style option switches to actual switch objects.
767
+ attr_reader :long
768
+
769
+ # List of all switches and summary string.
770
+ attr_reader :list
771
+
772
+ #
773
+ # Just initializes all instance variables.
774
+ #
775
+ def initialize
776
+ @atype = {}
777
+ @short = OptionMap.new
778
+ @long = OptionMap.new
779
+ @list = []
780
+ end
781
+
782
+ #
783
+ # See OptionParser.accept.
784
+ #
785
+ def accept(t, pat = /.*/m, &block)
786
+ if pat
787
+ pat.respond_to?(:match) ||
788
+ raise(TypeError, "has no `match'", ParseError.filter_backtrace(caller(2)))
789
+ else
790
+ pat = t if t.respond_to?(:match)
791
+ end
792
+ unless block
793
+ block = pat.method(:convert).to_proc if pat.respond_to?(:convert)
794
+ end
795
+ @atype[t] = [pat, block]
796
+ end
797
+
798
+ #
799
+ # See OptionParser.reject.
800
+ #
801
+ def reject(t)
802
+ @atype.delete(t)
803
+ end
804
+
805
+ #
806
+ # Adds +sw+ according to +sopts+, +lopts+ and +nlopts+.
807
+ #
808
+ # +sw+:: OptionParser::Switch instance to be added.
809
+ # +sopts+:: Short style option list.
810
+ # +lopts+:: Long style option list.
811
+ # +nlopts+:: Negated long style options list.
812
+ #
813
+ def update(sw, sopts, lopts, nsw = nil, nlopts = nil) # :nodoc:
814
+ sopts.each { |o| @short[o] = sw } if sopts
815
+ lopts.each { |o| @long[o] = sw } if lopts
816
+ nlopts.each { |o| @long[o] = nsw } if nsw && nlopts
817
+ used = @short.invert.update(@long.invert)
818
+ @list.delete_if { |o| (Switch === o) && !used[o] }
819
+ end
820
+ private :update
821
+
822
+ #
823
+ # Inserts +switch+ at the head of the list, and associates short, long
824
+ # and negated long options. Arguments are:
825
+ #
826
+ # +switch+:: OptionParser::Switch instance to be inserted.
827
+ # +short_opts+:: List of short style options.
828
+ # +long_opts+:: List of long style options.
829
+ # +nolong_opts+:: List of long style options with "no-" prefix.
830
+ #
831
+ # prepend(switch, short_opts, long_opts, nolong_opts)
832
+ #
833
+ def prepend(*args)
834
+ update(*args)
835
+ @list.unshift(args[0])
836
+ end
837
+
838
+ #
839
+ # Appends +switch+ at the tail of the list, and associates short, long
840
+ # and negated long options. Arguments are:
841
+ #
842
+ # +switch+:: OptionParser::Switch instance to be inserted.
843
+ # +short_opts+:: List of short style options.
844
+ # +long_opts+:: List of long style options.
845
+ # +nolong_opts+:: List of long style options with "no-" prefix.
846
+ #
847
+ # append(switch, short_opts, long_opts, nolong_opts)
848
+ #
849
+ def append(*args)
850
+ update(*args)
851
+ @list.push(args[0])
852
+ end
853
+
854
+ #
855
+ # Searches +key+ in +id+ list. The result is returned or yielded if a
856
+ # block is given. If it isn't found, nil is returned.
857
+ #
858
+ def search(id, key)
859
+ if list = __send__(id)
860
+ val = list.fetch(key) { return nil }
861
+ block_given? ? yield(val) : val
862
+ end
863
+ end
864
+
865
+ #
866
+ # Searches list +id+ for +opt+ and the optional patterns for completion
867
+ # +pat+. If +icase+ is true, the search is case insensitive. The result
868
+ # is returned or yielded if a block is given. If it isn't found, nil is
869
+ # returned.
870
+ #
871
+ def complete(id, opt, icase = false, *pat, &block)
872
+ __send__(id).complete(opt, icase, *pat, &block)
873
+ end
874
+
875
+ def get_candidates(id)
876
+ yield __send__(id).keys
877
+ end
878
+
879
+ #
880
+ # Iterates over each option, passing the option to the +block+.
881
+ #
882
+ def each_option(&block)
883
+ list.each(&block)
884
+ end
885
+
886
+ #
887
+ # Creates the summary table, passing each line to the +block+ (without
888
+ # newline). The arguments +args+ are passed along to the summarize
889
+ # method which is called on every option.
890
+ #
891
+ def summarize(*args, &block)
892
+ sum = []
893
+ list.reverse_each do |opt|
894
+ if opt.respond_to?(:summarize) # perhaps OptionParser::Switch
895
+ s = []
896
+ opt.summarize(*args) { |l| s << l }
897
+ sum.concat(s.reverse)
898
+ elsif !opt || opt.empty?
899
+ sum << ''
900
+ elsif opt.respond_to?(:each_line)
901
+ sum.concat([*opt.each_line].reverse)
902
+ else
903
+ sum.concat([*opt.each].reverse)
904
+ end
905
+ end
906
+ sum.reverse_each(&block)
907
+ end
908
+
909
+ def add_banner(to) # :nodoc:
910
+ list.each do |opt|
911
+ if opt.respond_to?(:add_banner)
912
+ opt.add_banner(to)
913
+ end
914
+ end
915
+ to
916
+ end
917
+
918
+ def compsys(*args, &block) # :nodoc:
919
+ list.each do |opt|
920
+ if opt.respond_to?(:compsys)
921
+ opt.compsys(*args, &block)
922
+ end
923
+ end
924
+ end
925
+ end
926
+
927
+ #
928
+ # Hash with completion search feature. See OptionParser::Completion.
929
+ #
930
+ class CompletingHash < Hash
931
+ include Completion
932
+
933
+ #
934
+ # Completion for hash key.
935
+ #
936
+ def match(key)
937
+ *values = fetch(key) do
938
+ raise AmbiguousArgument, catch(:ambiguous) { return complete(key) }
939
+ end
940
+ [key, *values]
941
+ end
942
+ end
943
+
944
+ # :stopdoc:
945
+
946
+ #
947
+ # Enumeration of acceptable argument styles. Possible values are:
948
+ #
949
+ # NO_ARGUMENT:: The switch takes no arguments. (:NONE)
950
+ # REQUIRED_ARGUMENT:: The switch requires an argument. (:REQUIRED)
951
+ # OPTIONAL_ARGUMENT:: The switch requires an optional argument. (:OPTIONAL)
952
+ #
953
+ # Use like --switch=argument (long style) or -Xargument (short style). For
954
+ # short style, only portion matched to argument pattern is treated as
955
+ # argument.
956
+ #
957
+ ArgumentStyle = {}
958
+ NoArgument.each { |el| ArgumentStyle[el] = Switch::NoArgument }
959
+ RequiredArgument.each { |el| ArgumentStyle[el] = Switch::RequiredArgument }
960
+ OptionalArgument.each { |el| ArgumentStyle[el] = Switch::OptionalArgument }
961
+ ArgumentStyle.freeze
962
+
963
+ #
964
+ # Switches common used such as '--', and also provides default
965
+ # argument classes
966
+ #
967
+ DefaultList = List.new
968
+ DefaultList.short['-'] = Switch::NoArgument.new {}
969
+ DefaultList.long[''] = Switch::NoArgument.new { throw :terminate }
970
+
971
+
972
+ COMPSYS_HEADER = <<'XXX' # :nodoc:
973
+
974
+ typeset -A opt_args
975
+ local context state line
976
+
977
+ _arguments -s -S \
978
+ XXX
979
+
980
+ def compsys(to, name = File.basename($0)) # :nodoc:
981
+ to << "#compdef #{name}\n"
982
+ to << COMPSYS_HEADER
983
+ visit(:compsys, {}, {}) do |o, d|
984
+ to << %[ "#{o}[#{d.gsub(/[\"\[\]]/, '\\\\\&')}]" \\\n]
985
+ end
986
+ to << " '*:file:_files' && return 0\n"
987
+ end
988
+
989
+ #
990
+ # Default options for ARGV, which never appear in option summary.
991
+ #
992
+ Officious = {}
993
+
994
+ #
995
+ # --help
996
+ # Shows option summary.
997
+ #
998
+ Officious['help'] = proc do |parser|
999
+ Switch::NoArgument.new do |arg|
1000
+ puts parser.help
1001
+ exit
1002
+ end
1003
+ end
1004
+
1005
+ #
1006
+ # --*-completion-bash=WORD
1007
+ # Shows candidates for command line completion.
1008
+ #
1009
+ Officious['*-completion-bash'] = proc do |parser|
1010
+ Switch::RequiredArgument.new do |arg|
1011
+ puts parser.candidate(arg)
1012
+ exit
1013
+ end
1014
+ end
1015
+
1016
+ #
1017
+ # --*-completion-zsh[=NAME:FILE]
1018
+ # Creates zsh completion file.
1019
+ #
1020
+ Officious['*-completion-zsh'] = proc do |parser|
1021
+ Switch::OptionalArgument.new do |arg|
1022
+ parser.compsys(STDOUT, arg)
1023
+ exit
1024
+ end
1025
+ end
1026
+
1027
+ #
1028
+ # --version
1029
+ # Shows version string if Version is defined.
1030
+ #
1031
+ Officious['version'] = proc do |parser|
1032
+ Switch::OptionalArgument.new do |pkg|
1033
+ if pkg
1034
+ begin
1035
+ require 'optparse/version'
1036
+ rescue LoadError
1037
+ else
1038
+ show_version(*pkg.split(/,/)) ||
1039
+ abort("#{parser.program_name}: no version found in package #{pkg}")
1040
+ exit
1041
+ end
1042
+ end
1043
+ (v = parser.ver) || abort("#{parser.program_name}: version unknown")
1044
+ puts v
1045
+ exit
1046
+ end
1047
+ end
1048
+
1049
+ # :startdoc:
1050
+
1051
+ #
1052
+ # Class methods
1053
+ #
1054
+
1055
+ #
1056
+ # Initializes a new instance and evaluates the optional block in context
1057
+ # of the instance. Arguments +args+ are passed to #new, see there for
1058
+ # description of parameters.
1059
+ #
1060
+ # This method is *deprecated*, its behavior corresponds to the older #new
1061
+ # method.
1062
+ #
1063
+ def self.with(*args, &block)
1064
+ opts = new(*args)
1065
+ opts.instance_eval(&block)
1066
+ opts
1067
+ end
1068
+
1069
+ #
1070
+ # Returns an incremented value of +default+ according to +arg+.
1071
+ #
1072
+ def self.inc(arg, default = nil)
1073
+ case arg
1074
+ when Integer
1075
+ arg.nonzero?
1076
+ when nil
1077
+ default.to_i + 1
1078
+ end
1079
+ end
1080
+
1081
+ def inc(*args)
1082
+ self.class.inc(*args)
1083
+ end
1084
+
1085
+ #
1086
+ # Initializes the instance and yields itself if called with a block.
1087
+ #
1088
+ # +banner+:: Banner message.
1089
+ # +width+:: Summary width.
1090
+ # +indent+:: Summary indent.
1091
+ #
1092
+ def initialize(banner = nil, width = 32, indent = ' ' * 4)
1093
+ @stack = [DefaultList, List.new, List.new]
1094
+ @program_name = nil
1095
+ @banner = banner
1096
+ @summary_width = width
1097
+ @summary_indent = indent
1098
+ @default_argv = ARGV
1099
+ @require_exact = false
1100
+ add_officious
1101
+ yield self if block_given?
1102
+ end
1103
+
1104
+ def add_officious # :nodoc:
1105
+ list = base
1106
+ Officious.each do |opt, block|
1107
+ list.long[opt] ||= block.call(self)
1108
+ end
1109
+ end
1110
+
1111
+ #
1112
+ # Terminates option parsing. Optional parameter +arg+ is a string pushed
1113
+ # back to be the first non-option argument.
1114
+ #
1115
+ def terminate(arg = nil)
1116
+ self.class.terminate(arg)
1117
+ end
1118
+
1119
+ def self.terminate(arg = nil)
1120
+ throw :terminate, arg
1121
+ end
1122
+
1123
+ @stack = [DefaultList]
1124
+ def self.top
1125
+ DefaultList
1126
+ end
1127
+
1128
+ #
1129
+ # Directs to accept specified class +t+. The argument string is passed to
1130
+ # the block in which it should be converted to the desired class.
1131
+ #
1132
+ # +t+:: Argument class specifier, any object including Class.
1133
+ # +pat+:: Pattern for argument, defaults to +t+ if it responds to match.
1134
+ #
1135
+ # accept(t, pat, &block)
1136
+ #
1137
+ def accept(*args, &blk)
1138
+ top.accept(*args, &blk)
1139
+ end
1140
+
1141
+ #
1142
+ # See #accept.
1143
+ #
1144
+ def self.accept(*args, &blk)
1145
+ top.accept(*args, &blk)
1146
+ end
1147
+
1148
+ #
1149
+ # Directs to reject specified class argument.
1150
+ #
1151
+ # +t+:: Argument class specifier, any object including Class.
1152
+ #
1153
+ # reject(t)
1154
+ #
1155
+ def reject(*args, &blk)
1156
+ top.reject(*args, &blk)
1157
+ end
1158
+
1159
+ #
1160
+ # See #reject.
1161
+ #
1162
+ def self.reject(*args, &blk)
1163
+ top.reject(*args, &blk)
1164
+ end
1165
+
1166
+ #
1167
+ # Instance methods
1168
+ #
1169
+
1170
+ # Heading banner preceding summary.
1171
+ attr_writer :banner
1172
+
1173
+ # Program name to be emitted in error message and default banner,
1174
+ # defaults to $0.
1175
+ attr_writer :program_name
1176
+
1177
+ # Width for option list portion of summary. Must be Numeric.
1178
+ attr_accessor :summary_width
1179
+
1180
+ # Indentation for summary. Must be String (or have + String method).
1181
+ attr_accessor :summary_indent
1182
+
1183
+ # Strings to be parsed in default.
1184
+ attr_accessor :default_argv
1185
+
1186
+ # Whether to require that options match exactly (disallows providing
1187
+ # abbreviated long option as short option).
1188
+ attr_accessor :require_exact
1189
+
1190
+ #
1191
+ # Heading banner preceding summary.
1192
+ #
1193
+ def banner
1194
+ unless @banner
1195
+ @banner = +"Usage: #{program_name} [options]"
1196
+ visit(:add_banner, @banner)
1197
+ end
1198
+ @banner
1199
+ end
1200
+
1201
+ #
1202
+ # Program name to be emitted in error message and default banner, defaults
1203
+ # to $0.
1204
+ #
1205
+ def program_name
1206
+ @program_name || File.basename($0, '.*')
1207
+ end
1208
+
1209
+ # for experimental cascading :-)
1210
+ alias set_banner banner=
1211
+ alias set_program_name program_name=
1212
+ alias set_summary_width summary_width=
1213
+ alias set_summary_indent summary_indent=
1214
+
1215
+ # Version
1216
+ attr_writer :version
1217
+ # Release code
1218
+ attr_writer :release
1219
+
1220
+ #
1221
+ # Version
1222
+ #
1223
+ def version
1224
+ (defined?(@version) && @version) || (defined?(::Version) && ::Version)
1225
+ end
1226
+
1227
+ #
1228
+ # Release code
1229
+ #
1230
+ def release
1231
+ (defined?(@release) && @release) || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
1232
+ end
1233
+
1234
+ #
1235
+ # Returns version string from program_name, version and release.
1236
+ #
1237
+ def ver
1238
+ if v = version
1239
+ str = +"#{program_name} #{[v].join('.')}"
1240
+ str << " (#{v})" if v = release
1241
+ str
1242
+ end
1243
+ end
1244
+
1245
+ def warn(mesg = $!)
1246
+ super("#{program_name}: #{mesg}")
1247
+ end
1248
+
1249
+ def abort(mesg = $!)
1250
+ super("#{program_name}: #{mesg}")
1251
+ end
1252
+
1253
+ #
1254
+ # Subject of #on / #on_head, #accept / #reject
1255
+ #
1256
+ def top
1257
+ @stack[-1]
1258
+ end
1259
+
1260
+ #
1261
+ # Subject of #on_tail.
1262
+ #
1263
+ def base
1264
+ @stack[1]
1265
+ end
1266
+
1267
+ #
1268
+ # Pushes a new List.
1269
+ #
1270
+ def new
1271
+ @stack.push(List.new)
1272
+ if block_given?
1273
+ yield self
1274
+ else
1275
+ self
1276
+ end
1277
+ end
1278
+
1279
+ #
1280
+ # Removes the last List.
1281
+ #
1282
+ def remove
1283
+ @stack.pop
1284
+ end
1285
+
1286
+ #
1287
+ # Puts option summary into +to+ and returns +to+. Yields each line if
1288
+ # a block is given.
1289
+ #
1290
+ # +to+:: Output destination, which must have method <<. Defaults to [].
1291
+ # +width+:: Width of left side, defaults to @summary_width.
1292
+ # +max+:: Maximum length allowed for left side, defaults to +width+ - 1.
1293
+ # +indent+:: Indentation, defaults to @summary_indent.
1294
+ #
1295
+ def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
1296
+ nl = "\n"
1297
+ blk ||= proc { |l| to << (l.index(nl, -1) ? l : l + nl) }
1298
+ visit(:summarize, {}, {}, width, max, indent, &blk)
1299
+ to
1300
+ end
1301
+
1302
+ #
1303
+ # Returns option summary string.
1304
+ #
1305
+ def help
1306
+ summarize([banner.to_s.sub(/\n?\z/, "\n")]).join
1307
+ end
1308
+ alias to_s help
1309
+
1310
+ #
1311
+ # Returns option summary list.
1312
+ #
1313
+ def to_a
1314
+ summarize([banner.to_s.split(/^/)]).join
1315
+ end
1316
+
1317
+ #
1318
+ # Checks if an argument is given twice, in which case an ArgumentError is
1319
+ # raised. Called from OptionParser#switch only.
1320
+ #
1321
+ # +obj+:: New argument.
1322
+ # +prv+:: Previously specified argument.
1323
+ # +msg+:: Exception message.
1324
+ #
1325
+ def notwice(obj, prv, msg) # :nodoc:
1326
+ unless !prv || (prv == obj)
1327
+ raise(ArgumentError, "argument #{msg} given twice: #{obj}",
1328
+ ParseError.filter_backtrace(caller(2))
1329
+ )
1330
+ end
1331
+ obj
1332
+ end
1333
+ private :notwice
1334
+
1335
+ SPLAT_PROC = proc { |*a| a.length <= 1 ? a.first : a } # :nodoc:
1336
+
1337
+ # :call-seq:
1338
+ # make_switch(params, block = nil)
1339
+ #
1340
+ # :include: ../doc/optparse/creates_option.rdoc
1341
+ #
1342
+ def make_switch(opts, block = nil)
1343
+ short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
1344
+ ldesc, sdesc, desc, arg = [], [], []
1345
+ default_style = Switch::NoArgument
1346
+ default_pattern = nil
1347
+ klass = nil
1348
+ q, a = nil
1349
+ has_arg = false
1350
+
1351
+ opts.each do |o|
1352
+ # argument class
1353
+ next if search(:atype, o) do |pat, c|
1354
+ klass = notwice(o, klass, 'type')
1355
+ if not_style && (not_style != Switch::NoArgument)
1356
+ not_pattern, not_conv = pat, c
1357
+ else
1358
+ default_pattern, conv = pat, c
1359
+ end
1360
+ end
1361
+
1362
+ # directly specified pattern(any object possible to match)
1363
+ if !(String === o || Symbol === o) && o.respond_to?(:match)
1364
+ pattern = notwice(o, pattern, 'pattern')
1365
+ if pattern.respond_to?(:convert)
1366
+ conv = pattern.method(:convert).to_proc
1367
+ else
1368
+ conv = SPLAT_PROC
1369
+ end
1370
+ next
1371
+ end
1372
+
1373
+ # anything others
1374
+ case o
1375
+ when Proc, Method
1376
+ block = notwice(o, block, 'block')
1377
+ when Array, Hash
1378
+ case pattern
1379
+ when CompletingHash
1380
+ when nil
1381
+ pattern = CompletingHash.new
1382
+ conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert)
1383
+ else
1384
+ raise ArgumentError, 'argument pattern given twice'
1385
+ end
1386
+ o.each { |pat, *v| pattern[pat] = v.fetch(0) { pat } }
1387
+ when Module
1388
+ raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4))
1389
+ when *ArgumentStyle.keys
1390
+ style = notwice(ArgumentStyle[o], style, 'style')
1391
+ when /^--no-([^\[\]=\s]*)(.+)?/
1392
+ q, a = Regexp.last_match(1), Regexp.last_match(2)
1393
+ o = notwice(a ? Object : TrueClass, klass, 'type')
1394
+ not_pattern, not_conv = search(:atype, o) unless not_style
1395
+ not_style = (not_style || default_style).guess(arg = a) if a
1396
+ default_style = Switch::NoArgument
1397
+ default_pattern, conv = search(:atype, FalseClass) unless default_pattern
1398
+ ldesc << "--no-#{q}"
1399
+ q = q.downcase.tr('_', '-')
1400
+ long << "no-#{q}"
1401
+ nolong << q
1402
+ when /^--\[no-\]([^\[\]=\s]*)(.+)?/
1403
+ q, a = Regexp.last_match(1), Regexp.last_match(2)
1404
+ o = notwice(a ? Object : TrueClass, klass, 'type')
1405
+ if a
1406
+ default_style = default_style.guess(arg = a)
1407
+ default_pattern, conv = search(:atype, o) unless default_pattern
1408
+ end
1409
+ ldesc << "--[no-]#{q}"
1410
+ o = q.downcase.tr('_', '-')
1411
+ long << o
1412
+ not_pattern, not_conv = search(:atype, FalseClass) unless not_style
1413
+ not_style = Switch::NoArgument
1414
+ nolong << "no-#{o}"
1415
+ when /^--([^\[\]=\s]*)(.+)?/
1416
+ q, a = Regexp.last_match(1), Regexp.last_match(2)
1417
+ if a
1418
+ o = notwice(NilClass, klass, 'type')
1419
+ default_style = default_style.guess(arg = a)
1420
+ default_pattern, conv = search(:atype, o) unless default_pattern
1421
+ end
1422
+ ldesc << "--#{q}"
1423
+ # Opal patch:
1424
+ o = q.downcase.tr('_', '-')
1425
+ long << o
1426
+ when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
1427
+ q, a = Regexp.last_match(1), Regexp.last_match(2)
1428
+ o = notwice(Object, klass, 'type')
1429
+ if a
1430
+ default_style = default_style.guess(arg = a)
1431
+ default_pattern, conv = search(:atype, o) unless default_pattern
1432
+ else
1433
+ has_arg = true
1434
+ end
1435
+ sdesc << "-#{q}"
1436
+ short << Regexp.new(q)
1437
+ when /^-(.)(.+)?/
1438
+ q, a = Regexp.last_match(1), Regexp.last_match(2)
1439
+ if a
1440
+ o = notwice(NilClass, klass, 'type')
1441
+ default_style = default_style.guess(arg = a)
1442
+ default_pattern, conv = search(:atype, o) unless default_pattern
1443
+ end
1444
+ sdesc << "-#{q}"
1445
+ short << q
1446
+ when /^=/
1447
+ style = notwice(default_style.guess(arg = o), style, 'style')
1448
+ default_pattern, conv = search(:atype, Object) unless default_pattern
1449
+ else
1450
+ desc.push(o)
1451
+ end
1452
+ end
1453
+
1454
+ default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
1455
+ if !(short.empty? && long.empty?)
1456
+ if has_arg && (default_style == Switch::NoArgument)
1457
+ default_style = Switch::RequiredArgument
1458
+ end
1459
+ s = (style || default_style).new(pattern || default_pattern,
1460
+ conv, sdesc, ldesc, arg, desc, block
1461
+ )
1462
+ elsif !block
1463
+ if style || pattern
1464
+ raise ArgumentError, 'no switch given', ParseError.filter_backtrace(caller)
1465
+ end
1466
+ s = desc
1467
+ else
1468
+ short << pattern
1469
+ s = (style || default_style).new(pattern,
1470
+ conv, nil, nil, arg, desc, block
1471
+ )
1472
+ end
1473
+ [s, short, long,
1474
+ (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
1475
+ nolong]
1476
+ end
1477
+
1478
+ # :call-seq:
1479
+ # define(*params, &block)
1480
+ #
1481
+ # :include: ../doc/optparse/creates_option.rdoc
1482
+ #
1483
+ def define(*opts, &block)
1484
+ top.append(*(sw = make_switch(opts, block)))
1485
+ sw[0]
1486
+ end
1487
+
1488
+ # :call-seq:
1489
+ # on(*params, &block)
1490
+ #
1491
+ # :include: ../doc/optparse/creates_option.rdoc
1492
+ #
1493
+ def on(*opts, &block)
1494
+ define(*opts, &block)
1495
+ self
1496
+ end
1497
+ alias def_option define
1498
+
1499
+ # :call-seq:
1500
+ # define_head(*params, &block)
1501
+ #
1502
+ # :include: ../doc/optparse/creates_option.rdoc
1503
+ #
1504
+ def define_head(*opts, &block)
1505
+ top.prepend(*(sw = make_switch(opts, block)))
1506
+ sw[0]
1507
+ end
1508
+
1509
+ # :call-seq:
1510
+ # on_head(*params, &block)
1511
+ #
1512
+ # :include: ../doc/optparse/creates_option.rdoc
1513
+ #
1514
+ # The new option is added at the head of the summary.
1515
+ #
1516
+ def on_head(*opts, &block)
1517
+ define_head(*opts, &block)
1518
+ self
1519
+ end
1520
+ alias def_head_option define_head
1521
+
1522
+ # :call-seq:
1523
+ # define_tail(*params, &block)
1524
+ #
1525
+ # :include: ../doc/optparse/creates_option.rdoc
1526
+ #
1527
+ def define_tail(*opts, &block)
1528
+ base.append(*(sw = make_switch(opts, block)))
1529
+ sw[0]
1530
+ end
1531
+
1532
+ #
1533
+ # :call-seq:
1534
+ # on_tail(*params, &block)
1535
+ #
1536
+ # :include: ../doc/optparse/creates_option.rdoc
1537
+ #
1538
+ # The new option is added at the tail of the summary.
1539
+ #
1540
+ def on_tail(*opts, &block)
1541
+ define_tail(*opts, &block)
1542
+ self
1543
+ end
1544
+ alias def_tail_option define_tail
1545
+
1546
+ #
1547
+ # Add separator in summary.
1548
+ #
1549
+ def separator(string)
1550
+ top.append(string, nil, nil)
1551
+ end
1552
+
1553
+ #
1554
+ # Parses command line arguments +argv+ in order. When a block is given,
1555
+ # each non-option argument is yielded. When optional +into+ keyword
1556
+ # argument is provided, the parsed option values are stored there via
1557
+ # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
1558
+ # similar object).
1559
+ #
1560
+ # Returns the rest of +argv+ left unparsed.
1561
+ #
1562
+ def order(*argv, into: nil, &nonopt)
1563
+ argv = argv[0].dup if (argv.size == 1) && (Array === argv[0])
1564
+ order!(argv, into: into, &nonopt)
1565
+ end
1566
+
1567
+ #
1568
+ # Same as #order, but removes switches destructively.
1569
+ # Non-option arguments remain in +argv+.
1570
+ #
1571
+ def order!(argv = default_argv, into: nil, &nonopt)
1572
+ setter = ->(name, val) { into[name.to_sym] = val } if into
1573
+ parse_in_order(argv, setter, &nonopt)
1574
+ end
1575
+
1576
+ def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
1577
+ opt, arg, val, rest = nil
1578
+ nonopt ||= proc { |a| throw :terminate, a }
1579
+ argv.unshift(arg) if arg = catch(:terminate) do
1580
+ while arg = argv.shift
1581
+ case arg
1582
+ # long option
1583
+ when /\A--([^=]*)(?:=(.*))?/m
1584
+ opt, rest = Regexp.last_match(1), Regexp.last_match(2)
1585
+ opt = opt.tr('_', '-')
1586
+ begin
1587
+ sw, = complete(:long, opt, true)
1588
+ if require_exact && !sw.long.include?(arg)
1589
+ raise InvalidOption, arg
1590
+ end
1591
+ rescue ParseError
1592
+ raise $!.set_option(arg, true)
1593
+ end
1594
+ begin
1595
+ opt, cb, val = sw.parse(rest, argv) { |*exc| raise(*exc) }
1596
+ val = cb.call(val) if cb
1597
+ setter.call(sw.switch_name, val) if setter
1598
+ rescue ParseError
1599
+ raise $!.set_option(arg, rest)
1600
+ end
1601
+
1602
+ # short option
1603
+ when /\A-(.)((=).*|.+)?/m
1604
+ eq, rest, opt = Regexp.last_match(3), Regexp.last_match(2), Regexp.last_match(1)
1605
+ has_arg, val = eq, rest
1606
+ begin
1607
+ sw, = search(:short, opt)
1608
+ unless sw
1609
+ begin
1610
+ sw, = complete(:short, opt)
1611
+ # short option matched.
1612
+ val = arg.delete_prefix('-')
1613
+ has_arg = true
1614
+ rescue InvalidOption
1615
+ raise if require_exact
1616
+ # if no short options match, try completion with long
1617
+ # options.
1618
+ sw, = complete(:long, opt)
1619
+ eq ||= !rest
1620
+ end
1621
+ end
1622
+ rescue ParseError
1623
+ raise $!.set_option(arg, true)
1624
+ end
1625
+ begin
1626
+ opt, cb, val = sw.parse(val, argv) { |*exc| raise(*exc) if eq }
1627
+ rescue ParseError
1628
+ raise $!.set_option(arg, arg.length > 2)
1629
+ else
1630
+ raise InvalidOption, arg if has_arg && !eq && (arg == "-#{opt}")
1631
+ end
1632
+ begin
1633
+ argv.unshift(opt) if opt && (!rest || ((opt = opt.sub(/\A-*/, '-')) != '-'))
1634
+ val = cb.call(val) if cb
1635
+ setter.call(sw.switch_name, val) if setter
1636
+ rescue ParseError
1637
+ raise $!.set_option(arg, arg.length > 2)
1638
+ end
1639
+
1640
+ # non-option argument
1641
+ else
1642
+ catch(:prune) do
1643
+ visit(:each_option) do |sw0|
1644
+ sw = sw0
1645
+ sw.block.call(arg) if (Switch === sw) && sw.match_nonswitch?(arg)
1646
+ end
1647
+ nonopt.call(arg)
1648
+ end
1649
+ end
1650
+ end
1651
+
1652
+ nil
1653
+ end
1654
+
1655
+ visit(:search, :short, nil) { |sw| sw.block.call(*argv) unless sw.pattern }
1656
+
1657
+ argv
1658
+ end
1659
+ private :parse_in_order
1660
+
1661
+ #
1662
+ # Parses command line arguments +argv+ in permutation mode and returns
1663
+ # list of non-option arguments. When optional +into+ keyword
1664
+ # argument is provided, the parsed option values are stored there via
1665
+ # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
1666
+ # similar object).
1667
+ #
1668
+ def permute(*argv, into: nil)
1669
+ argv = argv[0].dup if (argv.size == 1) && (Array === argv[0])
1670
+ permute!(argv, into: into)
1671
+ end
1672
+
1673
+ #
1674
+ # Same as #permute, but removes switches destructively.
1675
+ # Non-option arguments remain in +argv+.
1676
+ #
1677
+ def permute!(argv = default_argv, into: nil)
1678
+ nonopts = []
1679
+ order!(argv, into: into, &nonopts.method(:<<))
1680
+ argv[0, 0] = nonopts
1681
+ argv
1682
+ end
1683
+
1684
+ #
1685
+ # Parses command line arguments +argv+ in order when environment variable
1686
+ # POSIXLY_CORRECT is set, and in permutation mode otherwise.
1687
+ # When optional +into+ keyword argument is provided, the parsed option
1688
+ # values are stored there via <code>[]=</code> method (so it can be Hash,
1689
+ # or OpenStruct, or other similar object).
1690
+ #
1691
+ def parse(*argv, into: nil)
1692
+ argv = argv[0].dup if (argv.size == 1) && (Array === argv[0])
1693
+ parse!(argv, into: into)
1694
+ end
1695
+
1696
+ #
1697
+ # Same as #parse, but removes switches destructively.
1698
+ # Non-option arguments remain in +argv+.
1699
+ #
1700
+ def parse!(argv = default_argv, into: nil)
1701
+ if ENV.include?('POSIXLY_CORRECT')
1702
+ order!(argv, into: into)
1703
+ else
1704
+ permute!(argv, into: into)
1705
+ end
1706
+ end
1707
+
1708
+ #
1709
+ # Wrapper method for getopts.rb.
1710
+ #
1711
+ # params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option")
1712
+ # # params["a"] = true # -a
1713
+ # # params["b"] = "1" # -b1
1714
+ # # params["foo"] = "1" # --foo
1715
+ # # params["bar"] = "x" # --bar x
1716
+ # # params["zot"] = "z" # --zot Z
1717
+ #
1718
+ def getopts(*args)
1719
+ argv = Array === args.first ? args.shift : default_argv
1720
+ single_options, *long_options = *args
1721
+
1722
+ result = {}
1723
+
1724
+ if single_options
1725
+ single_options.scan(/(.)(:)?/) do |opt, val|
1726
+ if val
1727
+ result[opt] = nil
1728
+ define("-#{opt} VAL")
1729
+ else
1730
+ result[opt] = false
1731
+ define("-#{opt}")
1732
+ end
1733
+ end
1734
+ end
1735
+
1736
+ long_options.each do |arg|
1737
+ arg, desc = arg.split(';', 2)
1738
+ opt, val = arg.split(':', 2)
1739
+ if val
1740
+ result[opt] = val.empty? ? nil : val
1741
+ define("--#{opt}=#{result[opt] || 'VAL'}", *[desc].compact)
1742
+ else
1743
+ result[opt] = false
1744
+ define("--#{opt}", *[desc].compact)
1745
+ end
1746
+ end
1747
+
1748
+ parse_in_order(argv, result.method(:[]=))
1749
+ result
1750
+ end
1751
+
1752
+ #
1753
+ # See #getopts.
1754
+ #
1755
+ def self.getopts(*args)
1756
+ new.getopts(*args)
1757
+ end
1758
+
1759
+ #
1760
+ # Traverses @stack, sending each element method +id+ with +args+ and
1761
+ # +block+.
1762
+ #
1763
+ def visit(id, *args, &block) # :nodoc:
1764
+ @stack.reverse_each do |el|
1765
+ el.__send__(id, *args, &block)
1766
+ end
1767
+ nil
1768
+ end
1769
+ private :visit
1770
+
1771
+ #
1772
+ # Searches +key+ in @stack for +id+ hash and returns or yields the result.
1773
+ #
1774
+ def search(id, key) # :nodoc:
1775
+ block_given = block_given?
1776
+ retval = nil
1777
+ visit(:search, id, key) do |k|
1778
+ retval = block_given ? yield(k) : k
1779
+ break
1780
+ end
1781
+ retval
1782
+ end
1783
+ private :search
1784
+
1785
+ #
1786
+ # Completes shortened long style option switch and returns pair of
1787
+ # canonical switch and switch descriptor OptionParser::Switch.
1788
+ #
1789
+ # +typ+:: Searching table.
1790
+ # +opt+:: Searching key.
1791
+ # +icase+:: Search case insensitive if true.
1792
+ # +pat+:: Optional pattern for completion.
1793
+ #
1794
+ def complete(typ, opt, icase = false, *pat) # :nodoc:
1795
+ if pat.empty?
1796
+ retval = nil
1797
+ search(typ, opt) { |sw| retval = [sw, opt]; break } # exact match or...
1798
+ return retval if retval
1799
+ end
1800
+ ambiguous = catch(:ambiguous) do
1801
+ retval = nil
1802
+ visit(:complete, typ, opt, icase, *pat) { |o, *sw| retval = sw; break }
1803
+ return retval if retval
1804
+ end
1805
+ exc = ambiguous ? AmbiguousOption : InvalidOption
1806
+ raise exc.new(opt, additional: method(:additional_message).curry[typ])
1807
+ end
1808
+ private :complete
1809
+
1810
+ #
1811
+ # Returns additional info.
1812
+ #
1813
+ def additional_message(typ, opt)
1814
+ return unless typ && opt && defined?(DidYouMean::SpellChecker)
1815
+ all_candidates = []
1816
+ visit(:get_candidates, typ) do |candidates|
1817
+ all_candidates.concat(candidates)
1818
+ end
1819
+ all_candidates.select! { |cand| cand.is_a?(String) }
1820
+ checker = DidYouMean::SpellChecker.new(dictionary: all_candidates)
1821
+ DidYouMean.formatter.message_for(all_candidates & checker.correct(opt))
1822
+ end
1823
+
1824
+ def candidate(word)
1825
+ list = []
1826
+ case word
1827
+ when '-'
1828
+ long = short = true
1829
+ when /\A--/
1830
+ word, arg = word.split(/=/, 2)
1831
+ argpat = Completion.regexp(arg, false) if arg && !arg.empty?
1832
+ long = true
1833
+ when /\A-/
1834
+ short = true
1835
+ end
1836
+ pat = Completion.regexp(word, long)
1837
+ visit(:each_option) do |opt|
1838
+ next unless Switch === opt
1839
+ opts = (long ? opt.long : []) + (short ? opt.short : [])
1840
+ opts = Completion.candidate(word, true, pat, &opts.method(:each)).map(&:first) if pat
1841
+ if /\A=/ =~ opt.arg
1842
+ opts.map! { |sw| sw + '=' }
1843
+ if arg && (CompletingHash === opt.pattern)
1844
+ if opts = opt.pattern.candidate(arg, false, argpat)
1845
+ opts.map!(&:last)
1846
+ end
1847
+ end
1848
+ end
1849
+ list.concat(opts)
1850
+ end
1851
+ list
1852
+ end
1853
+
1854
+ #
1855
+ # Loads options from file names as +filename+. Does nothing when the file
1856
+ # is not present. Returns whether successfully loaded.
1857
+ #
1858
+ # +filename+ defaults to basename of the program without suffix in a
1859
+ # directory ~/.options, then the basename with '.options' suffix
1860
+ # under XDG and Haiku standard places.
1861
+ #
1862
+ def load(filename = nil)
1863
+ unless filename
1864
+ basename = File.basename($0, '.*')
1865
+ begin
1866
+ return true if load(File.expand_path(basename, '~/.options'))
1867
+ rescue
1868
+ nil
1869
+ end
1870
+ basename << '.options'
1871
+ return [
1872
+ # XDG
1873
+ ENV['XDG_CONFIG_HOME'],
1874
+ '~/.config',
1875
+ *ENV['XDG_CONFIG_DIRS']&.split(File::PATH_SEPARATOR),
1876
+
1877
+ # Haiku
1878
+ '~/config/settings',
1879
+ ].any? do |dir|
1880
+ next if !dir || dir.empty?
1881
+ begin
1882
+ load(File.expand_path(basename, dir))
1883
+ rescue
1884
+ nil
1885
+ end
1886
+ end
1887
+ end
1888
+ begin
1889
+ parse(*IO.readlines(filename).each(&:chomp!))
1890
+ true
1891
+ rescue Errno::ENOENT, Errno::ENOTDIR
1892
+ false
1893
+ end
1894
+ end
1895
+
1896
+ #
1897
+ # Parses environment variable +env+ or its uppercase with splitting like a
1898
+ # shell.
1899
+ #
1900
+ # +env+ defaults to the basename of the program.
1901
+ #
1902
+ def environment(env = File.basename($0, '.*'))
1903
+ (env = ENV[env] || ENV[env.upcase]) || return
1904
+ require 'shellwords'
1905
+ parse(*Shellwords.shellwords(env))
1906
+ end
1907
+
1908
+ #
1909
+ # Acceptable argument classes
1910
+ #
1911
+
1912
+ #
1913
+ # Any string and no conversion. This is fall-back.
1914
+ #
1915
+ accept(Object) { |s,| s || s.nil? }
1916
+
1917
+ accept(NilClass) { |s,| s }
1918
+
1919
+ #
1920
+ # Any non-empty string, and no conversion.
1921
+ #
1922
+ accept(String, /.+/m) { |s, *| s }
1923
+
1924
+ #
1925
+ # Ruby/C-like integer, octal for 0-7 sequence, binary for 0b, hexadecimal
1926
+ # for 0x, and decimal for others; with optional sign prefix. Converts to
1927
+ # Integer.
1928
+ #
1929
+ decimal = '\d+(?:_\d+)*'
1930
+ binary = 'b[01]+(?:_[01]+)*'
1931
+ hex = 'x[\da-f]+(?:_[\da-f]+)*'
1932
+ octal = "0(?:[0-7]+(?:_[0-7]+)*|#{binary}|#{hex})?"
1933
+ integer = "#{octal}|#{decimal}"
1934
+
1935
+ accept(Integer, %r"\A[-+]?(?:#{integer})\z"io) do |s,|
1936
+ if s
1937
+ begin
1938
+ Integer(s)
1939
+ rescue ArgumentError
1940
+ raise OptionParser::InvalidArgument, s
1941
+ end
1942
+ end
1943
+ end
1944
+
1945
+ #
1946
+ # Float number format, and converts to Float.
1947
+ #
1948
+ float = "(?:#{decimal}(?=(.)?)(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
1949
+ floatpat = %r"\A[-+]?#{float}\z"io
1950
+ accept(Float, floatpat) { |s,| s.to_f if s }
1951
+
1952
+ #
1953
+ # Generic numeric format, converts to Integer for integer format, Float
1954
+ # for float format, and Rational for rational format.
1955
+ #
1956
+ real = "[-+]?(?:#{octal}|#{float})"
1957
+ accept(Numeric, /\A(#{real})(?:\/(#{real}))?\z/io) do |s, d, f, n,|
1958
+ if n
1959
+ Rational(d, n)
1960
+ elsif f
1961
+ Float(s)
1962
+ else
1963
+ Integer(s)
1964
+ end
1965
+ end
1966
+
1967
+ #
1968
+ # Decimal integer format, to be converted to Integer.
1969
+ #
1970
+ DecimalInteger = /\A[-+]?#{decimal}\z/io
1971
+ accept(DecimalInteger, DecimalInteger) do |s,|
1972
+ if s
1973
+ begin
1974
+ Integer(s, 10)
1975
+ rescue ArgumentError
1976
+ raise OptionParser::InvalidArgument, s
1977
+ end
1978
+ end
1979
+ end
1980
+
1981
+ #
1982
+ # Ruby/C like octal/hexadecimal/binary integer format, to be converted to
1983
+ # Integer.
1984
+ #
1985
+ OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))\z/io
1986
+ accept(OctalInteger, OctalInteger) do |s,|
1987
+ if s
1988
+ begin
1989
+ Integer(s, 8)
1990
+ rescue ArgumentError
1991
+ raise OptionParser::InvalidArgument, s
1992
+ end
1993
+ end
1994
+ end
1995
+
1996
+ #
1997
+ # Decimal integer/float number format, to be converted to Integer for
1998
+ # integer format, Float for float format.
1999
+ #
2000
+ DecimalNumeric = floatpat # decimal integer is allowed as float also.
2001
+ accept(DecimalNumeric, floatpat) do |s, f|
2002
+ if s
2003
+ begin
2004
+ if f
2005
+ Float(s)
2006
+ else
2007
+ Integer(s)
2008
+ end
2009
+ rescue ArgumentError
2010
+ raise OptionParser::InvalidArgument, s
2011
+ end
2012
+ end
2013
+ end
2014
+
2015
+ #
2016
+ # Boolean switch, which means whether it is present or not, whether it is
2017
+ # absent or not with prefix no-, or it takes an argument
2018
+ # yes/no/true/false/+/-.
2019
+ #
2020
+ yesno = CompletingHash.new
2021
+ %w[- no false].each { |el| yesno[el] = false }
2022
+ %w[+ yes true].each { |el| yesno[el] = true }
2023
+ yesno['nil'] = false # should be nil?
2024
+ accept(TrueClass, yesno) { |arg, val| val.nil? || val }
2025
+ #
2026
+ # Similar to TrueClass, but defaults to false.
2027
+ #
2028
+ accept(FalseClass, yesno) { |arg, val| !val.nil? && val }
2029
+
2030
+ #
2031
+ # List of strings separated by ",".
2032
+ #
2033
+ accept(Array) do |s,|
2034
+ if s
2035
+ s = s.split(',').collect { |ss| ss unless ss.empty? }
2036
+ end
2037
+ s
2038
+ end
2039
+
2040
+ #
2041
+ # Regular expression with options.
2042
+ #
2043
+ accept(Regexp, %r"\A/((?:\\.|[^\\])*)/([[:alpha:]]+)?\z|.*") do |all, s, o|
2044
+ f = 0
2045
+ if o
2046
+ f |= Regexp::IGNORECASE if /i/ =~ o
2047
+ f |= Regexp::MULTILINE if /m/ =~ o
2048
+ f |= Regexp::EXTENDED if /x/ =~ o
2049
+ k = o.delete('imx')
2050
+ k = nil if k.empty?
2051
+ end
2052
+ Regexp.new(s || all, f, k)
2053
+ end
2054
+
2055
+ #
2056
+ # Exceptions
2057
+ #
2058
+
2059
+ #
2060
+ # Base class of exceptions from OptionParser.
2061
+ #
2062
+ class ParseError < RuntimeError
2063
+ # Reason which caused the error.
2064
+ Reason = 'parse error'
2065
+
2066
+ def initialize(*args, additional: nil)
2067
+ @additional = additional
2068
+ @arg0, = args
2069
+ @args = args
2070
+ @reason = nil
2071
+ end
2072
+
2073
+ attr_reader :args
2074
+ attr_writer :reason
2075
+ attr_accessor :additional
2076
+
2077
+ #
2078
+ # Pushes back erred argument(s) to +argv+.
2079
+ #
2080
+ def recover(argv)
2081
+ argv[0, 0] = @args
2082
+ argv
2083
+ end
2084
+
2085
+ def self.filter_backtrace(array)
2086
+ unless $DEBUG
2087
+ array.delete_if(&%r"\A#{Regexp.quote(__FILE__)}:"o.method(:=~))
2088
+ end
2089
+ array
2090
+ end
2091
+
2092
+ def set_backtrace(array)
2093
+ super(self.class.filter_backtrace(array))
2094
+ end
2095
+
2096
+ def set_option(opt, eq)
2097
+ if eq
2098
+ @args[0] = opt
2099
+ else
2100
+ @args.unshift(opt)
2101
+ end
2102
+ self
2103
+ end
2104
+
2105
+ #
2106
+ # Returns error reason. Override this for I18N.
2107
+ #
2108
+ def reason
2109
+ @reason || self.class::Reason
2110
+ end
2111
+
2112
+ def inspect
2113
+ "#<#{self.class}: #{args.join(' ')}>"
2114
+ end
2115
+
2116
+ #
2117
+ # Default stringizing method to emit standard error message.
2118
+ #
2119
+ def message
2120
+ "#{reason}: #{args.join(' ')}#{additional[@arg0] if additional}"
2121
+ end
2122
+
2123
+ alias to_s message
2124
+ end
2125
+
2126
+ #
2127
+ # Raises when ambiguously completable string is encountered.
2128
+ #
2129
+ class AmbiguousOption < ParseError
2130
+ const_set(:Reason, 'ambiguous option')
2131
+ end
2132
+
2133
+ #
2134
+ # Raises when there is an argument for a switch which takes no argument.
2135
+ #
2136
+ class NeedlessArgument < ParseError
2137
+ const_set(:Reason, 'needless argument')
2138
+ end
2139
+
2140
+ #
2141
+ # Raises when a switch with mandatory argument has no argument.
2142
+ #
2143
+ class MissingArgument < ParseError
2144
+ const_set(:Reason, 'missing argument')
2145
+ end
2146
+
2147
+ #
2148
+ # Raises when switch is undefined.
2149
+ #
2150
+ class InvalidOption < ParseError
2151
+ const_set(:Reason, 'invalid option')
2152
+ end
2153
+
2154
+ #
2155
+ # Raises when the given argument does not match required format.
2156
+ #
2157
+ class InvalidArgument < ParseError
2158
+ const_set(:Reason, 'invalid argument')
2159
+ end
2160
+
2161
+ #
2162
+ # Raises when the given argument word can't be completed uniquely.
2163
+ #
2164
+ class AmbiguousArgument < InvalidArgument
2165
+ const_set(:Reason, 'ambiguous argument')
2166
+ end
2167
+
2168
+ #
2169
+ # Miscellaneous
2170
+ #
2171
+
2172
+ #
2173
+ # Extends command line arguments array (ARGV) to parse itself.
2174
+ #
2175
+ module Arguable
2176
+ #
2177
+ # Sets OptionParser object, when +opt+ is +false+ or +nil+, methods
2178
+ # OptionParser::Arguable#options and OptionParser::Arguable#options= are
2179
+ # undefined. Thus, there is no ways to access the OptionParser object
2180
+ # via the receiver object.
2181
+ #
2182
+ def options=(opt)
2183
+ unless @optparse = opt
2184
+ class << self
2185
+ undef_method(:options)
2186
+ undef_method(:options=)
2187
+ end
2188
+ end
2189
+ end
2190
+
2191
+ #
2192
+ # Actual OptionParser object, automatically created if nonexistent.
2193
+ #
2194
+ # If called with a block, yields the OptionParser object and returns the
2195
+ # result of the block. If an OptionParser::ParseError exception occurs
2196
+ # in the block, it is rescued, a error message printed to STDERR and
2197
+ # +nil+ returned.
2198
+ #
2199
+ def options
2200
+ @optparse ||= OptionParser.new
2201
+ @optparse.default_argv = self
2202
+ block_given? || (return @optparse)
2203
+ begin
2204
+ yield @optparse
2205
+ rescue ParseError
2206
+ @optparse.warn $!
2207
+ nil
2208
+ end
2209
+ end
2210
+
2211
+ #
2212
+ # Parses +self+ destructively in order and returns +self+ containing the
2213
+ # rest arguments left unparsed.
2214
+ #
2215
+ def order!(&blk)
2216
+ options.order!(self, &blk)
2217
+ end
2218
+
2219
+ #
2220
+ # Parses +self+ destructively in permutation mode and returns +self+
2221
+ # containing the rest arguments left unparsed.
2222
+ #
2223
+ def permute!
2224
+ options.permute!(self)
2225
+ end
2226
+
2227
+ #
2228
+ # Parses +self+ destructively and returns +self+ containing the
2229
+ # rest arguments left unparsed.
2230
+ #
2231
+ def parse!
2232
+ options.parse!(self)
2233
+ end
2234
+
2235
+ #
2236
+ # Substitution of getopts is possible as follows. Also see
2237
+ # OptionParser#getopts.
2238
+ #
2239
+ # def getopts(*args)
2240
+ # ($OPT = ARGV.getopts(*args)).each do |opt, val|
2241
+ # eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val"
2242
+ # end
2243
+ # rescue OptionParser::ParseError
2244
+ # end
2245
+ #
2246
+ def getopts(*args)
2247
+ options.getopts(self, *args)
2248
+ end
2249
+
2250
+ #
2251
+ # Initializes instance variable.
2252
+ #
2253
+ def self.extend_object(obj)
2254
+ super
2255
+ obj.instance_eval { @optparse = nil }
2256
+ end
2257
+
2258
+ def initialize(*args)
2259
+ super
2260
+ @optparse = nil
2261
+ end
2262
+ end
2263
+
2264
+ #
2265
+ # Acceptable argument classes. Now contains DecimalInteger, OctalInteger
2266
+ # and DecimalNumeric. See Acceptable argument classes (in source code).
2267
+ #
2268
+ module Acceptables
2269
+ const_set(:DecimalInteger, OptionParser::DecimalInteger)
2270
+ const_set(:OctalInteger, OptionParser::OctalInteger)
2271
+ const_set(:DecimalNumeric, OptionParser::DecimalNumeric)
2272
+ end
2273
+ end
2274
+
2275
+ # ARGV is arguable by OptionParser
2276
+ ARGV.extend(OptionParser::Arguable)
2277
+
2278
+ # An alias for OptionParser.
2279
+ OptParse = OptionParser # :nodoc: