clasp-ruby 0.22.1 → 0.23.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +63 -52
  3. data/examples/cr-example.rb +16 -16
  4. data/examples/flag_and_option_specifications.md +25 -25
  5. data/examples/flag_and_option_specifications.rb +6 -6
  6. data/examples/show_usage_and_version.md +5 -5
  7. data/examples/show_usage_and_version.rb +1 -1
  8. data/examples/simple_command_line_no_specifications.rb +1 -1
  9. data/lib/clasp/arguments.rb +543 -531
  10. data/lib/clasp/clasp.rb +7 -7
  11. data/lib/clasp/cli.rb +140 -135
  12. data/lib/clasp/doc_.rb +3 -3
  13. data/lib/clasp/old_module.rb +3 -3
  14. data/lib/clasp/specifications.rb +337 -333
  15. data/lib/clasp/util/exceptions.rb +17 -17
  16. data/lib/clasp/util/value_parser.rb +97 -97
  17. data/lib/clasp/version.rb +15 -15
  18. data/lib/clasp-ruby.rb +3 -2
  19. data/lib/clasp.rb +3 -2
  20. data/test/scratch/test_list_command_line.rb +6 -6
  21. data/test/scratch/test_specifications.rb +14 -14
  22. data/test/scratch/test_usage.rb +6 -6
  23. data/test/scratch/test_usage_from_DATA.rb +1 -1
  24. data/test/scratch/test_usage_with_duplicate_specifications.rb +6 -6
  25. data/test/unit/tc_ARGV_rewrite.rb +36 -38
  26. data/test/unit/tc_arguments_1.rb +702 -656
  27. data/test/unit/tc_arguments_2.rb +52 -53
  28. data/test/unit/tc_arguments_3.rb +77 -77
  29. data/test/unit/tc_arguments_inspect.rb +55 -56
  30. data/test/unit/tc_cli.rb +4 -4
  31. data/test/unit/tc_default_value.rb +91 -91
  32. data/test/unit/tc_defaults_1.rb +38 -38
  33. data/test/unit/tc_examples_Arguments.rb +130 -132
  34. data/test/unit/tc_extras.rb +24 -26
  35. data/test/unit/tc_option_required.rb +38 -39
  36. data/test/unit/tc_option_value_aliases.rb +44 -44
  37. data/test/unit/tc_specifications.rb +7 -8
  38. data/test/unit/tc_typed_options.rb +204 -204
  39. data/test/unit/tc_usage.rb +112 -55
  40. data/test/unit/tc_with_action.rb +23 -24
  41. data/test/unit/ts_all.rb +1 -1
  42. metadata +8 -7
@@ -6,12 +6,13 @@
6
6
  # CLASP.Ruby
7
7
  #
8
8
  # Created: 14th February 2014
9
- # Updated: 20th April 2019
9
+ # Updated: 20th January 2024
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/CLASP.Ruby
12
12
  #
13
13
  # Author: Matthew Wilson
14
14
  #
15
+ # Copyright (c) 2019-2024, Matthew Wilson and Synesis Information Systems
15
16
  # Copyright (c) 2014-2019, Matthew Wilson and Synesis Software
16
17
  # All rights reserved.
17
18
  #
@@ -62,704 +63,715 @@ module CLASP
62
63
  # The main class for processing command-line arguments
63
64
  class Arguments
64
65
 
65
- # Class that represents a parsed flag
66
- class FlagArgument
67
-
68
- # @!visibility private
69
- #
70
- # [PRIVATE] This method is subject to changed between versions and
71
- # should not be called directly from application code
72
- def initialize(arg, given_index, given_name, resolved_name, argument_spec, given_hyphens, given_label, extras) # :nodoc:
73
-
74
- @arg = arg
75
- @given_index = given_index
76
- @given_name = given_name
77
- @argument_specification = argument_spec
78
- @given_hyphens = given_hyphens
79
- @given_label = given_label
80
- @name = resolved_name || given_name
81
- @extras = extras.nil? ? {} : extras
82
- end
83
-
84
- # (Integer) The command-line index of the argument
85
- attr_reader :given_index
86
- # (String) The given name of the argument as it appeared in the command-line
87
- attr_reader :given_name
88
- # (CLASP::FlagSpecification) The specification matching the argument, or +nil+
89
- attr_reader :argument_specification
90
- # (Integer) The number of hyphens of the argument as it appeared in the command-line
91
- attr_reader :given_hyphens
92
- # (String) The label of the argument as it appeared in the command-line
93
- attr_reader :given_label
94
- # (String) The resolved name of the argument
95
- attr_reader :name
96
- # (Object, Hash) The extras associated with the argument
97
- attr_reader :extras
98
-
99
- # [DEPRECATED] Use +argument_specification+
100
- def argument_alias; @argument_specification; end
101
-
102
- # (String) The string form of the flag, which is the same as +name+
103
- def to_s
104
-
105
- @name
106
- end
107
-
108
- # @!visibility private
109
- def eql?(rhs) # :nodoc:
110
-
111
- return false if rhs.nil?
112
-
113
- # check name and aliases
114
- return true if @name == rhs
115
- return argument_specification.aliases.include? rhs if argument_specification
116
- false
117
- end
118
-
119
- # @!visibility private
120
- def ==(rhs) # :nodoc:
121
-
122
- return false if rhs.nil?
123
- if not rhs.instance_of? String
124
-
125
- rhs = rhs.name
126
- end
127
- eql? rhs
128
- end
129
-
130
- # A hash-code for this instance
131
- def hash
132
-
133
- @arg.hash
134
- end
135
- end
136
-
137
- # Class that represents a parsed option
138
- class OptionArgument
139
-
140
- include ::CLASP::Util::ValueParser
141
-
142
- # @!visibility private
143
- #
144
- # [PRIVATE] This method is subject to changed between versions and
145
- # should not be called directly from application code
146
- def initialize(arg, given_index, given_name, resolved_name, argument_spec, given_hyphens, given_label, value, extras) # :nodoc:
147
-
148
- resolved_value = nil
149
-
150
- if argument_spec
151
-
152
- case constraint = (argument_spec.constraint || {})
66
+ # Class that represents a parsed flag
67
+ class FlagArgument
68
+
69
+ # @!visibility private
70
+ #
71
+ # [PRIVATE] This method is subject to changed between versions and
72
+ # should not be called directly from application code
73
+ def initialize(arg, given_index, given_name, resolved_name, argument_spec, given_hyphens, given_label, extras) # :nodoc:
74
+
75
+ @arg = arg
76
+ @given_index = given_index
77
+ @given_name = given_name
78
+ @argument_specification = argument_spec
79
+ @given_hyphens = given_hyphens
80
+ @given_label = given_label
81
+ @name = resolved_name || given_name
82
+ @extras = extras.nil? ? {} : extras
83
+ end
84
+
85
+ # (Integer) The command-line index of the argument
86
+ attr_reader :given_index
87
+ # (String) The given name of the argument as it appeared in the command-line
88
+ attr_reader :given_name
89
+ # (CLASP::FlagSpecification) The specification matching the argument, or +nil+
90
+ attr_reader :argument_specification
91
+ # (Integer) The number of hyphens of the argument as it appeared in the command-line
92
+ attr_reader :given_hyphens
93
+ # (String) The label of the argument as it appeared in the command-line
94
+ attr_reader :given_label
95
+ # (String) The resolved name of the argument
96
+ attr_reader :name
97
+ # (Object, Hash) The extras associated with the argument
98
+ attr_reader :extras
99
+
100
+ # [DEPRECATED] Use +argument_specification+
101
+ def argument_alias; @argument_specification; end
102
+
103
+ # (String) The string form of the flag, which is the same as +name+
104
+ def to_s
105
+
106
+ @name
107
+ end
108
+
109
+ # @!visibility private
110
+ def eql?(rhs) # :nodoc:
111
+
112
+ return false if rhs.nil?
113
+
114
+ # check name and aliases
115
+ return true if @name == rhs
116
+ return argument_specification.aliases.include? rhs if argument_specification
117
+ false
118
+ end
119
+
120
+ # @!visibility private
121
+ def ==(rhs) # :nodoc:
122
+
123
+ return false if rhs.nil?
124
+ if not rhs.instance_of? String
125
+
126
+ rhs = rhs.name
127
+ end
128
+ eql? rhs
129
+ end
130
+
131
+ # A hash-code for this instance
132
+ def hash
133
+
134
+ @arg.hash
135
+ end
136
+ end
137
+
138
+ # Class that represents a parsed option
139
+ class OptionArgument
140
+
141
+ include ::CLASP::Util::ValueParser
142
+
143
+ # @!visibility private
144
+ #
145
+ # [PRIVATE] This method is subject to changed between versions and
146
+ # should not be called directly from application code
147
+ def initialize(arg, given_index, given_name, resolved_name, argument_spec, given_hyphens, given_label, value, extras) # :nodoc:
148
+
149
+ resolved_value = nil
150
+
151
+ if argument_spec
152
+
153
+ case constraint = (argument_spec.constraint || {})
153
154
  =begin
154
- when Proc
155
+ when Proc
155
156
 
156
- resolved_value = value_from_Proc(constraint, value, arg, given_index, given_name, argument_spec, extras)
157
+ resolved_value = value_from_Proc(constraint, value, arg, given_index, given_name, argument_spec, extras)
157
158
  =end
158
- when Hash
159
-
160
- if constraint.empty?
161
-
162
- resolved_value = (value || '').empty? ? argument_spec.default_value : value
163
- else
164
-
165
- resolved_value = value_from_Hash(constraint, value, arg, given_index, given_name, argument_spec, extras)
166
- end
167
- else
168
-
169
- warn "unexpected constraint on argument specification #{argument_spec} when parsing argument '#{arg}'"
170
- end
171
- else
172
-
173
- resolved_value = value
174
- end
175
-
176
- @arg = arg
177
- @given_index = given_index
178
- @given_name = given_name
179
- @argument_specification = argument_spec
180
- @given_hyphens = given_hyphens
181
- @given_label = given_label
182
- @given_value = value
183
- @value = resolved_value
184
- @name = resolved_name || given_name
185
- @extras = extras.nil? ? {} : extras
186
- end
187
-
188
- # (Integer) The command-line index of the argument
189
- attr_reader :given_index
190
- # (String) The given name of the argument as it appeared in the command-line
191
- attr_reader :given_name
192
- # (CLASP::OptionSpecification) The specification matching the argument, or +nil+
193
- attr_reader :argument_specification
194
- # (Integer) The number of hyphens of the argument as it appeared in the command-line
195
- attr_reader :given_hyphens
196
- # (String) The label of the argument as it appeared in the command-line
197
- attr_reader :given_label
198
- # (String) The resolved name of the argument
199
- attr_reader :name
200
- # (String) The given value of the option
201
- attr_reader :given_value
202
- # (????) The value of the option, which may be of a type other than string subject to the option specification's constraint
203
- attr_reader :value
204
- # (Object, Hash) The extras associated with the argument
205
- attr_reader :extras
206
-
207
- # [DEPRECATED] Use +argument_specification+
208
- def argument_alias; @argument_specification; end
209
-
210
- # @!visibility private
211
- def eql?(rhs) # :nodoc:
212
-
213
- return false if rhs.nil?
214
-
215
- # check name and aliases
216
- return true if @name == rhs
217
- return argument_specification.aliases.include? rhs if argument_specification
218
- false
219
- end
159
+ when Hash
160
+
161
+ if constraint.empty?
162
+
163
+ resolved_value = (value || '').empty? ? argument_spec.default_value : value
164
+ else
165
+
166
+ resolved_value = value_from_Hash(constraint, value, arg, given_index, given_name, argument_spec, extras)
167
+ end
168
+ else
169
+
170
+ warn "unexpected constraint on argument specification #{argument_spec} when parsing argument '#{arg}'"
171
+ end
172
+ else
173
+
174
+ resolved_value = value
175
+ end
176
+
177
+ @arg = arg
178
+ @given_index = given_index
179
+ @given_name = given_name
180
+ @argument_specification = argument_spec
181
+ @given_hyphens = given_hyphens
182
+ @given_label = given_label
183
+ @given_value = value
184
+ @value = resolved_value
185
+ @name = resolved_name || given_name
186
+ @extras = extras.nil? ? {} : extras
187
+ end
188
+
189
+ # (Integer) The command-line index of the argument
190
+ attr_reader :given_index
191
+ # (String) The given name of the argument as it appeared in the command-line
192
+ attr_reader :given_name
193
+ # (CLASP::OptionSpecification) The specification matching the argument, or +nil+
194
+ attr_reader :argument_specification
195
+ # (Integer) The number of hyphens of the argument as it appeared in the command-line
196
+ attr_reader :given_hyphens
197
+ # (String) The label of the argument as it appeared in the command-line
198
+ attr_reader :given_label
199
+ # (String) The resolved name of the argument
200
+ attr_reader :name
201
+ # (String) The given value of the option
202
+ attr_reader :given_value
203
+ # (????) The value of the option, which may be of a type other than string subject to the option specification's constraint
204
+ attr_reader :value
205
+ # (Object, Hash) The extras associated with the argument
206
+ attr_reader :extras
207
+
208
+ # [DEPRECATED] Use +argument_specification+
209
+ def argument_alias; @argument_specification; end
210
+
211
+ # @!visibility private
212
+ def eql?(rhs) # :nodoc:
213
+
214
+ return false if rhs.nil?
215
+
216
+ # check name and aliases
217
+ return true if @name == rhs
218
+ return argument_specification.aliases.include? rhs if argument_specification
219
+ false
220
+ end
220
221
 
221
- # @!visibility private
222
- def ==(rhs) # :nodoc:
222
+ # @!visibility private
223
+ def ==(rhs) # :nodoc:
223
224
 
224
- return false if rhs.nil?
225
- if not rhs.instance_of? String
225
+ return false if rhs.nil?
226
+ if not rhs.instance_of? String
226
227
 
227
- rhs = rhs.name
228
- end
229
- eql? rhs
230
- end
228
+ rhs = rhs.name
229
+ end
230
+ eql? rhs
231
+ end
231
232
 
232
- # A hash-code for this instance
233
- def hash
233
+ # A hash-code for this instance
234
+ def hash
234
235
 
235
- @arg.hash
236
- end
236
+ @arg.hash
237
+ end
237
238
 
238
- # (String) The string form of the flag, which is the same as +name+=+value+
239
- def to_s
239
+ # (String) The string form of the flag, which is the same as +name+=+value+
240
+ def to_s
240
241
 
241
- "#{name}=#{value}"
242
- end
243
- end
242
+ "#{name}=#{value}"
243
+ end
244
+ end
244
245
 
245
- # ######################
246
- # Construction
246
+ # ######################
247
+ # Construction
247
248
 
248
- # Loads an instance of the class, as specified by +source+, according to the given parameters
249
- #
250
- # See the documentation for the ::CLASP module for examples
251
- #
252
- # === Signature
253
- #
254
- # * *Parameters:*
255
- # - +argv+ (+Array+) The arguments array. May not be +nil+. Defaults to +ARGV+
256
- # - +source+ (+Hash+, +IO+) The arguments specification, either as a Hash or an instance of an IO-implementing type containing a YAML specification
257
- # - +options+ An options hash, containing any of the following options
258
- #
259
- # * *Options:*
260
- # - +mutate_argv:+ (+Boolean+) Determines if the library should mutate +argv+. Defaults to +true+. This is essential when using CLASP in conjunction with <tt>$\<</tt>
261
- #
262
- def self.load(argv, source, options = {}) # :yields: An instance of +CLASP::Arguments+
249
+ # Loads an instance of the class, as specified by +source+, according to the given parameters
250
+ #
251
+ # See the documentation for the ::CLASP module for examples
252
+ #
253
+ # === Signature
254
+ #
255
+ # * *Parameters:*
256
+ # - +argv+ (+Array+) The arguments array. May not be +nil+. Defaults to +ARGV+
257
+ # - +source+ (+Hash+, +IO+) The arguments specification, either as a Hash or an instance of an IO-implementing type containing a YAML specification
258
+ # - +options+ An options hash, containing any of the following options
259
+ #
260
+ # * *Options:*
261
+ # - +:mutate_argv+ (+Boolean+) Determines if the library should mutate +argv+. Defaults to +true+. This is essential when using CLASP in conjunction with <tt>$\<</tt>
262
+ #
263
+ def self.load(argv, source, options = {}) # :yields: An instance of +CLASP::Arguments+
263
264
 
264
- options ||= {}
265
+ options ||= {}
265
266
 
266
- specs = load_specifications(source, options)
267
+ specs = load_specifications(source, options)
267
268
 
268
- self.new argv, specs, options
269
- end
269
+ self.new argv, specs, options
270
+ end
270
271
 
271
- # Loads the specifications as specified by +source+, according to the given parameters
272
- #
273
- # === Signature
274
- #
275
- # * *Parameters:*
276
- # - +source+ (+Hash+, +IO+) The arguments specification, either as a Hash or an instance of an IO-implementing type containing a YAML specification
277
- # - +options+ An options hash, containing any of the following options
278
- def self.load_specifications(source, options = {}) # :yields: An array of specification instances
272
+ # Loads the specifications as specified by +source+, according to the given parameters
273
+ #
274
+ # === Signature
275
+ #
276
+ # * *Parameters:*
277
+ # - +source+ (+Hash+, +IO+) The arguments specification, either as a Hash or an instance of an IO-implementing type containing a YAML specification
278
+ # - +options+ An options hash, containing any of the following options
279
+ def self.load_specifications(source, options = {}) # :yields: An array of specification instances
279
280
 
280
- options ||= {}
281
+ options ||= {}
281
282
 
282
- h = nil
283
+ h = nil
283
284
 
284
- case source
285
- when ::IO
285
+ case source
286
+ when ::IO
286
287
 
287
- h = YAML.load(source.read)
288
- when ::Hash
288
+ h = YAML.load(source.read)
289
+ when ::Hash
289
290
 
290
- h = source
291
- else
291
+ h = source
292
+ else
292
293
 
293
- if source.respond_to?(:to_hash)
294
+ if source.respond_to?(:to_hash)
294
295
 
295
- h = source.to_hash
296
- else
296
+ h = source.to_hash
297
+ else
297
298
 
298
- raise TypeError, "#{self}.#{__method__}() 'source' argument must be a #{::Hash}, or an object implementing #{::IO}, or a type implementing to_hash'"
299
- end
300
- end
299
+ raise TypeError, "#{self}.#{__method__}() 'source' argument must be a #{::Hash}, or an object implementing #{::IO}, or a type implementing to_hash'"
300
+ end
301
+ end
301
302
 
302
- specs = []
303
+ specs = []
303
304
 
304
- _clasp = h['clasp'] or raise ArgumentError, "missing top-level 'clasp' element in load configuration"
305
- ::Hash === _clasp or raise ArgumentError, "top-level 'clasp' element must be a #{::Hash}"
305
+ _clasp = h['clasp'] or raise ArgumentError, "missing top-level 'clasp' element in load configuration"
306
+ ::Hash === _clasp or raise ArgumentError, "top-level 'clasp' element must be a #{::Hash}"
306
307
 
307
- _specs = (_clasp['arg-specs'] || _clasp['specifications'] || _clasp['aliases']) or raise ArgumentError, "missing element 'clasp/specifications'"
308
- ::Array === _specs or raise ArgumentError, "top-level 'specifications' element must be a #{::Hash}"
308
+ _specs = (_clasp['arg-specs'] || _clasp['specifications'] || _clasp['aliases']) or raise ArgumentError, "missing element 'clasp/specifications'"
309
+ ::Array === _specs or raise ArgumentError, "top-level 'specifications' element must be a #{::Hash}"
309
310
 
310
- _specs.each do |_spec|
311
+ _specs.each do |_spec|
311
312
 
312
- case _spec
313
- when ::Hash
313
+ case _spec
314
+ when ::Hash
314
315
 
315
- # TODO: make a utility function and shrink all the following
316
+ # TODO: make a utility function and shrink all the following
316
317
 
317
- _spec.each do |_arg_type, _details|
318
+ _spec.each do |_arg_type, _details|
318
319
 
319
- case _arg_type
320
- when 'flag', :flag
320
+ case _arg_type
321
+ when 'flag', :flag
321
322
 
322
- _name = _details['name']
323
+ _name = _details['name']
323
324
 
324
- unless _name
325
+ unless _name
325
326
 
326
- warn "flag specification missing required 'name' field"
327
- else
327
+ warn "flag specification missing required 'name' field"
328
+ else
328
329
 
329
- _alias = _details['alias']
330
- _aliases = _details['aliases']
331
- _help = _details['help'] || _details['description']
330
+ _alias = _details['alias']
331
+ _aliases = _details['aliases']
332
+ _help = _details['help'] || _details['description']
332
333
 
333
- specs << CLASP.Flag(_name, alias: _alias, aliases: _aliases, help: _help)
334
- end
335
- when 'option', :option
334
+ specs << CLASP.Flag(_name, alias: _alias, aliases: _aliases, help: _help)
335
+ end
336
+ when 'option', :option
336
337
 
337
- _name = _details['name']
338
+ _name = _details['name']
338
339
 
339
- unless _name
340
+ unless _name
340
341
 
341
- warn "option specification missing required 'name' field"
342
- else
342
+ warn "option specification missing required 'name' field"
343
+ else
343
344
 
344
- _alias = _details['alias']
345
- _aliases = _details['aliases']
346
- _default_value = _details['default_value'] || _details['default']
347
- _help = _details['help'] || _details['description']
348
- _required = _details['required']
349
- _required_message = _details['required_message']
350
- _values_range = _details['values_range'] || _details['values']
345
+ _alias = _details['alias']
346
+ _aliases = _details['aliases']
347
+ _default_value = _details['default_value'] || _details['default']
348
+ _help = _details['help'] || _details['description']
349
+ _required = _details['required']
350
+ _required_message = _details['required_message']
351
+ _values_range = _details['values_range'] || _details['values']
351
352
 
352
- specs << CLASP.Option(_name, alias: _alias, aliases: _aliases, default_value: _default_value, help: _help, required: _required, required_message: _required_message, values_range: _values_range)
353
- end
354
- when 'alias', :alias
353
+ specs << CLASP.Option(_name, alias: _alias, aliases: _aliases, default_value: _default_value, help: _help, required: _required, required_message: _required_message, values_range: _values_range)
354
+ end
355
+ when 'alias', :alias
355
356
 
356
- _resolved = _details['resolved']
357
+ _resolved = _details['resolved']
357
358
 
358
- unless _resolved
359
+ unless _resolved
359
360
 
360
- warn "alias specification missing required 'resolved' field"
361
- else
361
+ warn "alias specification missing required 'resolved' field"
362
+ else
362
363
 
363
- _alias = _details['alias']
364
- _aliases = _details['aliases']
364
+ _alias = _details['alias']
365
+ _aliases = _details['aliases']
365
366
 
366
- unless _alias || _aliases
367
+ unless _alias || _aliases
367
368
 
368
- warn "alias specification missing required 'alias' or 'aliases' field"
369
- else
369
+ warn "alias specification missing required 'alias' or 'aliases' field"
370
+ else
370
371
 
371
- specs << CLASP.Flag(_resolved, alias: _alias, aliases: _aliases)
372
- end
373
- end
374
- else
372
+ specs << CLASP.Flag(_resolved, alias: _alias, aliases: _aliases)
373
+ end
374
+ end
375
+ else
375
376
 
376
- warn "unknown arg-type '#{_arg_type}' specified"
377
- end
378
- end
379
- else
377
+ warn "unknown arg-type '#{_arg_type}' specified"
378
+ end
379
+ end
380
+ else
380
381
 
381
- warn "non-#{::Hash} element in 'clasp/specifications': #{_spec} (of type #{_spec.class})"
382
- end
383
- end
382
+ warn "non-#{::Hash} element in 'clasp/specifications': #{_spec} (of type #{_spec.class})"
383
+ end
384
+ end
384
385
 
385
- specs
386
- end
386
+ specs
387
+ end
387
388
 
388
- # Constructs an instance of the class, according to the given parameters
389
- #
390
- # See the documentation for the ::CLASP module for examples
391
- #
392
- # === Signature
393
- #
394
- # * *Parameters:*
395
- # - +argv+ (+Array+) The arguments array. May not be +nil+. Defaults to +ARGV+
396
- # - +specifications+ (+Array+) The specifications array. Defaults to +nil+. If none supplied, no aliasing will be performed
397
- # - +options+ An options hash, containing any of the following options
398
- #
399
- # * *Options:*
400
- # - +mutate_argv:+ (+Boolean+) Determines if the library should mutate +argv+. Defaults to +true+. This is essential when using CLASP in conjunction with <tt>$\<</tt>
401
- #
402
- def initialize(argv = ARGV, specifications = nil, options = {})
389
+ # Constructs an instance of the class, according to the given parameters
390
+ #
391
+ # See the documentation for the ::CLASP module for examples
392
+ #
393
+ # === Signature
394
+ #
395
+ # * *Parameters:*
396
+ # - +argv+ (+Array+) The arguments array. May not be +nil+. Defaults to +ARGV+
397
+ # - +specifications+ (+Array+) The specifications array. Defaults to +nil+. If none supplied, no aliasing will be performed
398
+ # - +options+ An options hash, containing any of the following options
399
+ #
400
+ # * *Options:*
401
+ # - +:mutate_argv+ (+Boolean+) Determines if the library should mutate +argv+. Defaults to +true+. This is essential when using CLASP in conjunction with <tt>$\<</tt>
402
+ #
403
+ def initialize(argv = ARGV, specifications = nil, options = {})
403
404
 
404
- # have to do this name-swap, as 'options' has CLASP-specific
405
- # meaning
406
- init_opts, options = options.dup, nil
405
+ # have to do this name-swap, as 'options' has CLASP-specific
406
+ # meaning
407
+ init_opts, options = options.dup, nil
407
408
 
408
- init_opts[:mutate_argv] = true unless init_opts.has_key? :mutate_argv
409
+ init_opts[:mutate_argv] = true unless init_opts.has_key? :mutate_argv
409
410
 
410
- @program_name = init_opts[:program_name] || Arguments.derive_program_name_
411
+ @program_name = init_opts[:program_name] || Arguments.derive_program_name_
411
412
 
412
- @argv = argv
413
- argv = argv.dup
414
- @argv_original_copy = argv.dup.freeze
413
+ @argv = argv
414
+ argv = argv.dup
415
+ @argv_original_copy = argv.dup.freeze
415
416
 
416
- @specifications = specifications
417
- @aliases = @specifications
417
+ @specifications = specifications
418
+ @aliases = @specifications
418
419
 
419
- specifications = nil if specifications and specifications.empty?
420
+ specifications = nil if specifications and specifications.empty?
420
421
 
421
- flags, options, values = Arguments.parse_(argv, specifications)
422
+ flags, options, values, double_slash_index = Arguments.parse_(argv, specifications)
422
423
 
423
- [ flags, options, values ].each do |ar|
424
+ [ flags, options, values ].each do |ar|
424
425
 
425
- class << ar
426
+ class << ar
426
427
 
427
- undef :inspect
428
- undef :to_s
428
+ undef :inspect
429
+ undef :to_s
429
430
 
430
- def to_s
431
+ def to_s
431
432
 
432
- s = ''
433
+ s = ''
433
434
 
434
- s += '['
435
- s += self.map { |v| %Q<"#{v}"> }.join(', ')
436
- s += ']'
435
+ s += '['
436
+ s += self.map { |v| %Q<"#{v}"> }.join(', ')
437
+ s += ']'
437
438
 
438
- s
439
- end
439
+ s
440
+ end
440
441
 
441
- def inspect
442
+ def inspect
442
443
 
443
- s = ''
444
+ s = ''
444
445
 
445
- s += "#<#{self.class}:0x#{(object_id << 1).to_s(16)} ["
446
- s += self.map { |v| v.inspect }.join(', ')
447
- s += "]>"
446
+ s += "#<#{self.class}:0x#{(object_id << 1).to_s(16)} ["
447
+ s += self.map { |v| v.inspect }.join(', ')
448
+ s += "]>"
448
449
 
449
- s
450
- end
451
- end
452
- end
450
+ s
451
+ end
452
+ end
453
+ end
453
454
 
454
- @flags = flags.freeze
455
- @options = options.freeze
456
- @values = values.freeze
455
+ @flags = flags.freeze
456
+ @options = options.freeze
457
+ @values = values.freeze
457
458
 
458
- # do argv-mutation, if required
459
- if init_opts[:mutate_argv]
459
+ @double_slash_index = double_slash_index
460
460
 
461
- while not argv.empty?
461
+ # do argv-mutation, if required
462
+ if init_opts[:mutate_argv]
462
463
 
463
- argv.shift
464
- end
464
+ while not argv.empty?
465
465
 
466
- @values.each do |v|
466
+ argv.shift
467
+ end
467
468
 
468
- argv << v
469
- end
470
- end
471
- end
469
+ @values.each do |v|
472
470
 
473
- private
474
- # @!visibility private
475
- def self.derive_program_name_ # :nodoc:
471
+ argv << v
472
+ end
473
+ end
474
+ end
476
475
 
477
- $0
478
- end
476
+ private
477
+ # @!visibility private
478
+ def self.derive_program_name_ # :nodoc:
479
479
 
480
- # @!visibility private
481
- def self.parse_(argv, specifications) # :nodoc:
480
+ $0
481
+ end
482
482
 
483
- flags = []
484
- options = []
485
- values = []
483
+ # @!visibility private
484
+ def self.parse_(argv, specifications) # :nodoc:
486
485
 
487
- forced_value = false
488
- pending_option = nil
486
+ flags = []
487
+ options = []
488
+ values = []
489
489
 
490
- argv.each_with_index do |arg, index|
490
+ double_slash_index = nil
491
491
 
492
- if not forced_value
492
+ forced_value = false
493
+ pending_option = nil
493
494
 
494
- if '--' == arg
495
+ argv.each_with_index do |arg, index|
495
496
 
496
- # all subsequent arguments are values
497
- forced_value = true
498
- next
499
- end
497
+ if not forced_value
500
498
 
501
- # do regex test to see if option/flag/value
502
- if arg =~ /^(-+)([^=]+)/
499
+ if '--' == arg
503
500
 
504
- hyphens = $1
505
- given_label = $2
506
- given_name = "#$1#$2"
507
- value = ($' and not $'.empty?) ? $'[1 ... $'.size] : nil
508
- argument_spec = nil
509
- resolved_name = nil
501
+ # all subsequent arguments are values
502
+ forced_value = true
510
503
 
511
- (specifications || []).each do |s|
504
+ double_slash_index = index if double_slash_index.nil?
512
505
 
513
- if s.name == given_name or s.aliases.include? given_name
506
+ next
507
+ end
514
508
 
515
- argument_spec = s
516
- resolved_name = s.name
509
+ # do regex test to see if option/flag/value
510
+ if arg =~ /^(-+)([^=]+)/
517
511
 
518
- # need to check whether the alias is a default-option
519
- # and, if so, expand out its name and value, and replace
520
- # the name and (if none previously specified) the value
521
- if resolved_name =~ /^(-+)([^=]+)=/
512
+ hyphens = $1
513
+ given_label = $2
514
+ given_name = "#$1#$2"
515
+ value = ($' and not $'.empty?) ? $'[1 ... $'.size] : nil
516
+ argument_spec = nil
517
+ resolved_name = nil
522
518
 
523
- resolved_name = "#$1#$2"
524
- value ||= $'
519
+ (specifications || []).each do |s|
525
520
 
526
- # now find the underlying (option) specification
527
- specifications.each do |t|
521
+ if s.name == given_name or s.aliases.include? given_name
528
522
 
529
- if t.name == resolved_name or t.aliases.include? resolved_name
523
+ argument_spec = s
524
+ resolved_name = s.name
530
525
 
531
- argument_spec = t
526
+ # need to check whether the alias is a default-option
527
+ # and, if so, expand out its name and value, and replace
528
+ # the name and (if none previously specified) the value
529
+ if resolved_name =~ /^(-+)([^=]+)=/
532
530
 
533
- break
534
- end
535
- end
536
- end
531
+ resolved_name = "#$1#$2"
532
+ value ||= $'
537
533
 
538
- break
539
- end
540
- end
534
+ # now find the underlying (option) specification
535
+ specifications.each do |t|
541
536
 
542
- # Here we intercept and (potentially) cater to grouped flags
543
- if not argument_spec and not value and specifications and 1 == hyphens.size
537
+ if t.name == resolved_name or t.aliases.include? resolved_name
544
538
 
545
- # Must match all
546
- flag_aliases = []
547
- given_label[0 ... given_label.size].each_char do |c|
539
+ argument_spec = t
548
540
 
549
- new_flag = "-#{c.chr}"
541
+ break
542
+ end
543
+ end
544
+ end
550
545
 
551
- flag_alias = nil
546
+ break
547
+ end
548
+ end
552
549
 
553
- # special case where the flag's actual name is short form and found here
554
- flag_alias ||= specifications.detect { |s| s.is_a?(CLASP::FlagSpecification) && s.name == new_flag }
550
+ # Here we intercept and (potentially) cater to grouped flags
551
+ if not argument_spec and not value and specifications and 1 == hyphens.size
555
552
 
556
- # if not found as a flag, look in each specifications' aliases
557
- flag_alias ||= specifications.detect { |s| s.aliases.include? new_flag }
553
+ # Must match all
554
+ flag_aliases = []
555
+ given_label[0 ... given_label.size].each_char do |c|
558
556
 
559
- if not flag_alias
557
+ new_flag = "-#{c.chr}"
560
558
 
561
- flag_aliases = nil
562
- break
563
- else
559
+ flag_alias = nil
564
560
 
565
- flag_aliases << flag_alias
566
- end
567
- end
561
+ # special case where the flag's actual name is short form and found here
562
+ flag_alias ||= specifications.detect { |s| s.is_a?(CLASP::FlagSpecification) && s.name == new_flag }
568
563
 
569
- if flag_aliases
564
+ # if not found as a flag, look in each specifications' aliases
565
+ flag_alias ||= specifications.detect { |s| s.aliases.include? new_flag }
570
566
 
571
- # got them all, so now have to process them all
572
- # as normal. Note: is this susceptible to
573
- # infinite recursion
567
+ if not flag_alias
574
568
 
575
- # convert to argv and invoke
576
- flags_argv = flag_aliases.map { |s| s.name }
569
+ flag_aliases = nil
570
+ break
571
+ else
577
572
 
578
- grp_flags, grp_options, grp_value = Arguments.parse_(flags_argv, specifications)
573
+ flag_aliases << flag_alias
574
+ end
575
+ end
579
576
 
580
- grp_flags.map! { |f| FlagArgument.new(arg, index, given_name, f.name, f.argument_specification, hyphens.size, given_label, argument_spec ? argument_spec.extras : nil) }
581
- grp_options.map! { |o| OptionArgument.new(arg, index, given_name, o.name, o.argument_specification, hyphens.size, given_label, o.value, argument_spec ? argument_spec.extras : nil) }
577
+ if flag_aliases
582
578
 
583
- flags.push(*grp_flags)
584
- options.push(*grp_options)
585
- values.push(*grp_value)
579
+ # got them all, so now have to process them all
580
+ # as normal. Note: is this susceptible to
581
+ # infinite recursion
586
582
 
587
- next
588
- end
589
- end
583
+ # convert to argv and invoke
584
+ flags_argv = flag_aliases.map { |s| s.name }
590
585
 
591
- if argument_spec and argument_spec.is_a? CLASP::OptionSpecification and not value
586
+ grp_flags, grp_options, grp_value, grp_double_slash_index = Arguments.parse_(flags_argv, specifications)
592
587
 
593
- pending_option = {
588
+ grp_flags.map! { |f| FlagArgument.new(arg, index, given_name, f.name, f.argument_specification, hyphens.size, given_label, argument_spec ? argument_spec.extras : nil) }
589
+ grp_options.map! { |o| OptionArgument.new(arg, index, given_name, o.name, o.argument_specification, hyphens.size, given_label, o.value, argument_spec ? argument_spec.extras : nil) }
594
590
 
595
- arg: arg,
596
- index: index,
597
- given_name: given_name,
598
- resolved_name: resolved_name,
599
- argument_spec: argument_spec,
600
- hyphens_size: hyphens.size,
601
- given_label: given_label,
602
- extras: argument_spec ? argument_spec.extras : nil,
603
- }
604
- elsif value
591
+ flags.push(*grp_flags)
592
+ options.push(*grp_options)
593
+ values.push(*grp_value)
605
594
 
606
- options << OptionArgument.new(arg, index, given_name, resolved_name, argument_spec, hyphens.size, given_label, value, argument_spec ? argument_spec.extras : nil)
607
- else
595
+ next
596
+ end
597
+ end
608
598
 
609
- flags << FlagArgument.new(arg, index, given_name, resolved_name, argument_spec, hyphens.size, given_label, argument_spec ? argument_spec.extras : nil)
610
- end
599
+ if argument_spec and argument_spec.is_a? CLASP::OptionSpecification and not value
611
600
 
612
- next
613
- end
614
- end
601
+ pending_option = {
615
602
 
616
- if pending_option
603
+ arg: arg,
604
+ index: index,
605
+ given_name: given_name,
606
+ resolved_name: resolved_name,
607
+ argument_spec: argument_spec,
608
+ hyphens_size: hyphens.size,
609
+ given_label: given_label,
610
+ extras: argument_spec ? argument_spec.extras : nil,
611
+ }
612
+ elsif value
617
613
 
618
- value = forced_value ? nil : arg
614
+ options << OptionArgument.new(arg, index, given_name, resolved_name, argument_spec, hyphens.size, given_label, value, argument_spec ? argument_spec.extras : nil)
615
+ else
619
616
 
620
- options << OptionArgument.new(pending_option[:arg], pending_option[:index], pending_option[:given_name], pending_option[:resolved_name], pending_option[:argument_spec], pending_option[:hyphens_size], pending_option[:given_label], value, pending_option[:extras])
617
+ flags << FlagArgument.new(arg, index, given_name, resolved_name, argument_spec, hyphens.size, given_label, argument_spec ? argument_spec.extras : nil)
618
+ end
621
619
 
622
- pending_option = nil
620
+ next
621
+ end
622
+ end
623
623
 
624
- next unless forced_value
625
- end
624
+ if pending_option
626
625
 
627
- arg = arg.dup
628
- arg_ix = ::Integer === index ? index : index.dup
626
+ value = forced_value ? nil : arg
629
627
 
630
- arg.define_singleton_method(:given_index) { arg_ix }
628
+ options << OptionArgument.new(pending_option[:arg], pending_option[:index], pending_option[:given_name], pending_option[:resolved_name], pending_option[:argument_spec], pending_option[:hyphens_size], pending_option[:given_label], value, pending_option[:extras])
631
629
 
632
- values << arg
633
- end
630
+ pending_option = nil
634
631
 
635
- if pending_option
632
+ next unless forced_value
633
+ end
636
634
 
637
- value = nil
635
+ arg = arg.dup
636
+ arg_ix = ::Integer === index ? index : index.dup
638
637
 
639
- options << OptionArgument.new(pending_option[:arg], pending_option[:index], pending_option[:given_name], pending_option[:resolved_name], pending_option[:argument_spec], pending_option[:hyphens_size], pending_option[:given_label], value, pending_option[:extras])
638
+ arg.define_singleton_method(:given_index) { arg_ix }
640
639
 
641
- end
640
+ values << arg
641
+ end
642
642
 
643
- return flags, options, values
644
- end
643
+ if pending_option
645
644
 
646
- # ######################
647
- # Attributes
645
+ value = nil
648
646
 
649
- public
650
- # (Array) a frozen array of specifications
651
- attr_reader :specifications
647
+ options << OptionArgument.new(pending_option[:arg], pending_option[:index], pending_option[:given_name], pending_option[:resolved_name], pending_option[:argument_spec], pending_option[:hyphens_size], pending_option[:given_label], value, pending_option[:extras])
652
648
 
653
- # [DEPRECATED] Instead refer to +specifications+
654
- attr_reader :aliases
649
+ end
655
650
 
656
- # (Array) a frozen array of flags
657
- attr_reader :flags
651
+ return flags, options, values, double_slash_index
652
+ end
658
653
 
659
- # (Array) a frozen array of options
660
- attr_reader :options
654
+ # ######################
655
+ # Attributes
661
656
 
662
- # (Array) a frozen array of values
663
- attr_reader :values
657
+ public
658
+ # (Array) a frozen array of specifications
659
+ attr_reader :specifications
664
660
 
665
- # (Array) the (possibly mutated) array of arguments instance passed to new
666
- attr_reader :argv
661
+ # [DEPRECATED] Instead refer to +specifications+
662
+ attr_reader :aliases
667
663
 
668
- # (Array) unchanged copy of the original array of arguments passed to new
669
- attr_reader :argv_original_copy
664
+ # (Array) a frozen array of flags
665
+ attr_reader :flags
670
666
 
671
- # (String) The program name
672
- attr_reader :program_name
667
+ # (Array) a frozen array of options
668
+ attr_reader :options
673
669
 
674
- # Finds the first unknown flag or option; +nil+ if all used
675
- #
676
- # === Signature
677
- #
678
- # * *Parameters:*
679
- # - +options+ (Hash) options
680
- #
681
- # * *Options:*
682
- # - +:specifications+ ([CLASP::Specification]) Array of specifications. If not specified, the instance's +specifications+ attribute is used
683
- #
684
- # === Return
685
- # (CLASP::Arguments::OptionArgument) The first unknown option; +nil+ if none found
686
- def find_first_unknown options = {}
670
+ # (Array) a frozen array of values
671
+ attr_reader :values
687
672
 
688
- option = {} if options.nil?
673
+ # (Integer, +nil+) index of the first '--', if present; +nil+ otherwise
674
+ attr_reader :double_slash_index
689
675
 
690
- raise ArgumentError, "options must be nil or Hash - #{option.class} given" unless options.is_a? ::Hash
676
+ # (Array) the (possibly mutated) array of arguments instance passed to new
677
+ attr_reader :argv
691
678
 
692
- specifications = options[:specifications] || options[:aliases] || @specifications
679
+ # (Array) unchanged copy of the original array of arguments passed to new
680
+ attr_reader :argv_original_copy
693
681
 
694
- raise ArgumentError, "specifications may not be nil" if specifications.nil?
682
+ # (String) The program name
683
+ attr_reader :program_name
695
684
 
696
- flags.each do |f|
685
+ # Finds the first unknown flag or option; +nil+ if all used
686
+ #
687
+ # === Signature
688
+ #
689
+ # * *Parameters:*
690
+ # - +options+ (Hash) options
691
+ #
692
+ # * *Options:*
693
+ # - +:specifications+ ([CLASP::Specification]) Array of specifications. If not specified, the instance's +specifications+ attribute is used
694
+ #
695
+ # === Return
696
+ # (CLASP::Arguments::OptionArgument) The first unknown option; +nil+ if none found
697
+ def find_first_unknown options = {}
697
698
 
698
- return f unless specifications.any? { |al| al.is_a?(::CLASP::FlagSpecification) && al.name == f.name }
699
- end
699
+ option = {} if options.nil?
700
700
 
701
- self.options.each do |o|
701
+ raise ArgumentError, "options must be nil or Hash - #{option.class} given" unless options.is_a? ::Hash
702
702
 
703
- return o unless specifications.any? { |al| al.is_a?(::CLASP::OptionSpecification) && al.name == o.name }
704
- end
703
+ specifications = options[:specifications] || options[:aliases] || @specifications
705
704
 
706
- nil
707
- end
705
+ raise ArgumentError, "specifications may not be nil" if specifications.nil?
708
706
 
709
- # Searches for a flag that matches the given id, returning the flag if
710
- # found; +nil+ otherwise
711
- #
712
- # === Signature
713
- #
714
- # * *Parameters:*
715
- # - +id+ (String, CLASP::FlagArgument) The name of a flag, or the flag itself
716
- #
717
- # === Return
718
- # (CLASP::Arguments::FlagArgument) The first flag matching +id+; +nil+ if none found
719
- def find_flag(id)
707
+ flags.each do |f|
720
708
 
721
- flags.each do |flag|
709
+ return f unless specifications.any? { |al| al.is_a?(::CLASP::FlagSpecification) && al.name == f.name }
710
+ end
722
711
 
723
- return flag if flag == id
724
- end
712
+ self.options.each do |o|
725
713
 
726
- nil
727
- end
714
+ return o unless specifications.any? { |al| al.is_a?(::CLASP::OptionSpecification) && al.name == o.name }
715
+ end
728
716
 
729
- # Searches for a option that matches the given id, returning the option
730
- # if found; +nil+ otherwise
731
- #
732
- # === Signature
733
- #
734
- # * *Parameter:*
735
- # - +id+ (String, CLASP::OptionArgument) The name of a option, or the option itself
736
- #
737
- # === Return
738
- # (CLASP::Arguments::OptionArgument) The first option matching +id+; +nil+ if none found
739
- def find_option(id)
717
+ nil
718
+ end
740
719
 
741
- options.each do |option|
720
+ # Searches for a flag that matches the given id, returning the flag if
721
+ # found; +nil+ otherwise
722
+ #
723
+ # === Signature
724
+ #
725
+ # * *Parameters:*
726
+ # - +id+ (String, CLASP::FlagArgument) The name of a flag, or the flag itself
727
+ #
728
+ # === Return
729
+ # (CLASP::Arguments::FlagArgument) The first flag matching +id+; +nil+ if none found
730
+ def find_flag(id)
742
731
 
743
- return option if option == id
744
- end
732
+ flags.each do |flag|
745
733
 
746
- nil
747
- end
734
+ return flag if flag == id
735
+ end
748
736
 
749
- # #################################################################### #
750
- # backwards-compatible
737
+ nil
738
+ end
751
739
 
752
- # @!visibility private
753
- Flag = FlagArgument # :nodoc:
754
- # @!visibility private
755
- Option = OptionArgument # :nodoc:
740
+ # Searches for a option that matches the given id, returning the option
741
+ # if found; +nil+ otherwise
742
+ #
743
+ # === Signature
744
+ #
745
+ # * *Parameter:*
746
+ # - +id+ (String, CLASP::OptionArgument) The name of a option, or the option itself
747
+ #
748
+ # === Return
749
+ # (CLASP::Arguments::OptionArgument) The first option matching +id+; +nil+ if none found
750
+ def find_option(id)
751
+
752
+ options.each do |option|
753
+
754
+ return option if option == id
755
+ end
756
+
757
+ nil
758
+ end
759
+
760
+ # #################################################################### #
761
+ # backwards-compatible
762
+
763
+ # @!visibility private
764
+ Flag = FlagArgument # :nodoc:
765
+ # @!visibility private
766
+ Option = OptionArgument # :nodoc:
756
767
  end # class Arguments
757
768
 
769
+
758
770
  # ######################################################################## #
759
771
  # module
760
772
 
761
773
  end # module CLASP
762
774
 
763
- # ############################## end of file ############################# #
764
775
 
776
+ # ############################## end of file ############################# #
765
777