libclimate-ruby 0.17.0 → 0.17.0.1
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.
- checksums.yaml +4 -4
- data/examples/flag_and_option_specifications.from_DATA.rb +4 -4
- data/examples/flag_and_option_specifications.rb +22 -22
- data/examples/show_usage_and_version.rb +7 -7
- data/lib/libclimate/climate.rb +1000 -1000
- data/lib/libclimate/version.rb +14 -14
- data/test/scratch/blankzeroes.rb +14 -14
- data/test/scratch/specifications.rb +23 -23
- data/test/unit/tc_abort.rb +42 -42
- data/test/unit/tc_double_slash_index.rb +73 -73
- data/test/unit/tc_infer_version.rb +47 -47
- data/test/unit/tc_minimal.rb +516 -516
- data/test/unit/tc_minimal_CLASP.rb +323 -323
- data/test/unit/tc_parse.rb +104 -104
- data/test/unit/tc_parse_and_verify.rb +93 -93
- data/test/unit/tc_test_specifications.rb +48 -48
- data/test/unit/tc_values.rb +294 -294
- data/test/unit/tc_with_blocks.rb +18 -18
- data/test/unit/tc_with_blocks_CLASP.rb +18 -18
- metadata +6 -6
data/lib/libclimate/climate.rb
CHANGED
@@ -5,13 +5,13 @@
|
|
5
5
|
# Purpose: Definition of the ::LibCLImate::Climate class
|
6
6
|
#
|
7
7
|
# Created: 13th July 2015
|
8
|
-
# Updated:
|
8
|
+
# Updated: 6th March 2025
|
9
9
|
#
|
10
10
|
# Home: http://github.com/synesissoftware/libCLImate.Ruby
|
11
11
|
#
|
12
12
|
# Author: Matthew Wilson
|
13
13
|
#
|
14
|
-
# Copyright (c) 2019-
|
14
|
+
# Copyright (c) 2019-2025, Matthew Wilson and Synesis Information Systems
|
15
15
|
# Copyright (c) 2015-2019, Matthew Wilson and Synesis Software
|
16
16
|
# All rights reserved.
|
17
17
|
#
|
@@ -55,13 +55,13 @@ require 'yaml'
|
|
55
55
|
# TODO: Need to work with other colouring libraries, too
|
56
56
|
if !defined? Colcon # :nodoc:
|
57
57
|
|
58
|
-
|
58
|
+
begin
|
59
59
|
|
60
|
-
|
61
|
-
|
60
|
+
require 'colcon'
|
61
|
+
rescue LoadError #=> x
|
62
62
|
|
63
|
-
|
64
|
-
|
63
|
+
warn "could not load colcon library" if $DEBUG
|
64
|
+
end
|
65
65
|
end
|
66
66
|
|
67
67
|
# We monkey-patch CLASP module's Flag and Option generator methods by
|
@@ -70,66 +70,66 @@ end
|
|
70
70
|
|
71
71
|
class << CLASP
|
72
72
|
|
73
|
-
|
74
|
-
|
73
|
+
alias_method :Flag_old, :Flag # :nodoc:
|
74
|
+
alias_method :Option_old, :Option # :nodoc:
|
75
75
|
|
76
|
-
|
77
|
-
|
76
|
+
# Defines a flag, attaching the given block
|
77
|
+
def Flag(name, options={}, &blk)
|
78
78
|
|
79
|
-
|
79
|
+
f = self.Flag_old(name, options)
|
80
80
|
|
81
|
-
|
82
|
-
|
81
|
+
# anticipate this functionality being added to CLASP
|
82
|
+
unless f.respond_to? :action
|
83
83
|
|
84
|
-
|
84
|
+
class << f
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
86
|
+
attr_accessor :action
|
87
|
+
end
|
88
|
+
end
|
89
89
|
|
90
|
-
|
90
|
+
if blk
|
91
91
|
|
92
|
-
|
93
|
-
|
94
|
-
|
92
|
+
case blk.arity
|
93
|
+
when 0, 1, 2
|
94
|
+
else
|
95
95
|
|
96
|
-
|
97
|
-
|
96
|
+
warn "wrong arity for flag"
|
97
|
+
end
|
98
98
|
|
99
|
-
|
100
|
-
|
99
|
+
f.action = blk
|
100
|
+
end
|
101
101
|
|
102
|
-
|
103
|
-
|
102
|
+
f
|
103
|
+
end
|
104
104
|
|
105
|
-
|
106
|
-
|
105
|
+
# Defines an option, attaching the given block
|
106
|
+
def Option(name, options={}, &blk)
|
107
107
|
|
108
|
-
|
108
|
+
o = self.Option_old(name, options)
|
109
109
|
|
110
|
-
|
111
|
-
|
110
|
+
# anticipate this functionality being added to CLASP
|
111
|
+
unless o.respond_to? :action
|
112
112
|
|
113
|
-
|
113
|
+
class << o
|
114
114
|
|
115
|
-
|
116
|
-
|
117
|
-
|
115
|
+
attr_accessor :action
|
116
|
+
end
|
117
|
+
end
|
118
118
|
|
119
|
-
|
119
|
+
if blk
|
120
120
|
|
121
|
-
|
122
|
-
|
123
|
-
|
121
|
+
case blk.arity
|
122
|
+
when 0, 1, 2
|
123
|
+
else
|
124
124
|
|
125
|
-
|
126
|
-
|
125
|
+
warn "wrong arity for option"
|
126
|
+
end
|
127
127
|
|
128
|
-
|
129
|
-
|
128
|
+
o.action = blk
|
129
|
+
end
|
130
130
|
|
131
|
-
|
132
|
-
|
131
|
+
o
|
132
|
+
end
|
133
133
|
end
|
134
134
|
|
135
135
|
#:startdoc:
|
@@ -171,1261 +171,1261 @@ module LibCLImate
|
|
171
171
|
#
|
172
172
|
class Climate
|
173
173
|
|
174
|
-
|
174
|
+
include ::Xqsr3::Quality::ParameterChecking
|
175
175
|
|
176
|
-
|
177
|
-
|
176
|
+
# Represents the results obtained from +Climate#parse()+
|
177
|
+
class ParseResults
|
178
178
|
|
179
|
-
|
179
|
+
def initialize(climate, arguments, options)
|
180
180
|
|
181
|
-
|
181
|
+
@climate = climate
|
182
182
|
|
183
|
-
|
183
|
+
@arguments = arguments
|
184
184
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
185
|
+
@argv = arguments.argv
|
186
|
+
@argv_original_copy = arguments.argv_original_copy
|
187
|
+
@specifications = arguments.specifications
|
188
|
+
@program_name = climate.program_name
|
189
|
+
@flags = arguments.flags
|
190
|
+
@options = arguments.options
|
191
|
+
@values = arguments.values
|
192
|
+
@double_slash_index = arguments.double_slash_index
|
193
|
+
end
|
194
194
|
|
195
|
-
|
196
|
-
|
195
|
+
# (Climate) The +Climate+ instance from which this instance was obtained
|
196
|
+
attr_reader :climate
|
197
197
|
|
198
|
-
|
199
|
-
|
198
|
+
# ([String]) The original arguments passed into the +Climate#parse()+ method
|
199
|
+
attr_reader :argv
|
200
200
|
|
201
|
-
|
202
|
-
|
201
|
+
# (Array) unchanged copy of the original array of arguments passed to parse
|
202
|
+
attr_reader :argv_original_copy
|
203
203
|
|
204
|
-
|
205
|
-
|
204
|
+
# (Array) a frozen array of specifications
|
205
|
+
attr_reader :specifications
|
206
206
|
|
207
|
-
|
208
|
-
|
207
|
+
# (String) The program name
|
208
|
+
attr_reader :program_name
|
209
209
|
|
210
|
-
|
211
|
-
|
210
|
+
# (String) A (frozen) array of flags
|
211
|
+
attr_reader :flags
|
212
212
|
|
213
|
-
|
214
|
-
|
213
|
+
# (String) A (frozen) array of options
|
214
|
+
attr_reader :options
|
215
215
|
|
216
|
-
|
217
|
-
|
216
|
+
# (String) A (frozen) array of values
|
217
|
+
attr_reader :values
|
218
218
|
|
219
|
-
|
219
|
+
attr_reader :double_slash_index
|
220
220
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
221
|
+
# Verifies the initiating command-line against the specifications,
|
222
|
+
# raising an exception if any missing, unused, or unrecognised flags,
|
223
|
+
# options, or values are found
|
224
|
+
def verify(**options)
|
225
225
|
|
226
|
-
|
226
|
+
if v = options[:raise]
|
227
227
|
|
228
|
-
|
228
|
+
hm = {}
|
229
229
|
|
230
|
-
|
231
|
-
|
232
|
-
|
230
|
+
hm[:raise_on_required] = v unless options.has_key?(:raise_on_required)
|
231
|
+
hm[:raise_on_unrecognised] = v unless options.has_key?(:raise_on_unrecognised)
|
232
|
+
hm[:raise_on_unused] = v unless options.has_key?(:raise_on_unused)
|
233
233
|
|
234
|
-
|
235
|
-
|
234
|
+
options = options.merge hm
|
235
|
+
end
|
236
236
|
|
237
|
-
|
238
|
-
|
239
|
-
|
237
|
+
raise_on_required = options[:raise_on_required]
|
238
|
+
raise_on_unrecognised = options[:raise_on_unrecognised]
|
239
|
+
raise_on_unused = options[:raise_on_unused]
|
240
240
|
|
241
241
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
242
|
+
# Verification:
|
243
|
+
#
|
244
|
+
# 1. Check arguments recognised
|
245
|
+
# 1.a Flags
|
246
|
+
# 1.b Options
|
247
|
+
# 2. police any required options
|
248
|
+
# 3. Check values
|
249
249
|
|
250
|
-
|
250
|
+
# 1.a Flags
|
251
251
|
|
252
|
-
|
252
|
+
self.flags.each do |flag|
|
253
253
|
|
254
|
-
|
254
|
+
spec = specifications.detect do |sp|
|
255
255
|
|
256
|
-
|
257
|
-
|
256
|
+
sp.kind_of?(::CLASP::FlagSpecification) && flag.name == sp.name
|
257
|
+
end
|
258
258
|
|
259
|
-
|
259
|
+
if spec
|
260
260
|
|
261
|
-
|
261
|
+
if spec.respond_to?(:action) && !spec.action.nil?
|
262
262
|
|
263
|
-
|
264
|
-
|
265
|
-
|
263
|
+
spec.action.call(flag, spec)
|
264
|
+
end
|
265
|
+
else
|
266
266
|
|
267
|
-
|
267
|
+
message = make_abort_message_("unrecognised flag '#{f}'")
|
268
268
|
|
269
|
-
|
269
|
+
if false
|
270
270
|
|
271
|
-
|
271
|
+
elsif climate.ignore_unknown
|
272
272
|
|
273
|
-
|
274
|
-
|
273
|
+
;
|
274
|
+
elsif raise_on_unrecognised
|
275
275
|
|
276
|
-
|
276
|
+
if raise_on_unrecognised.is_a?(Class)
|
277
277
|
|
278
|
-
|
279
|
-
|
278
|
+
raise raise_on_unrecognised, message
|
279
|
+
else
|
280
280
|
|
281
|
-
|
282
|
-
|
283
|
-
|
281
|
+
raise RuntimeError, message
|
282
|
+
end
|
283
|
+
elsif climate.exit_on_unknown
|
284
284
|
|
285
|
-
|
286
|
-
|
285
|
+
climate.abort message
|
286
|
+
else
|
287
287
|
|
288
|
-
|
288
|
+
if program_name && !program_name.empty?
|
289
289
|
|
290
|
-
|
291
|
-
|
290
|
+
message = "#{program_name}: #{message}"
|
291
|
+
end
|
292
292
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
293
|
+
climate.stderr.puts message
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
297
|
|
298
|
-
|
298
|
+
# 1.b Options
|
299
299
|
|
300
|
-
|
300
|
+
self.options.each do |option|
|
301
301
|
|
302
|
-
|
302
|
+
spec = specifications.detect do |sp|
|
303
303
|
|
304
|
-
|
305
|
-
|
304
|
+
sp.kind_of?(::CLASP::OptionSpecification) && option.name == sp.name
|
305
|
+
end
|
306
306
|
|
307
|
-
|
307
|
+
if spec
|
308
308
|
|
309
|
-
|
309
|
+
if spec.respond_to?(:action) && !spec.action.nil?
|
310
310
|
|
311
|
-
|
312
|
-
|
313
|
-
|
311
|
+
spec.action.call(option, spec)
|
312
|
+
end
|
313
|
+
else
|
314
314
|
|
315
|
-
|
315
|
+
message = make_abort_message_("unrecognised option '#{f}'")
|
316
316
|
|
317
|
-
|
317
|
+
if false
|
318
318
|
|
319
|
-
|
319
|
+
elsif climate.ignore_unknown
|
320
320
|
|
321
|
-
|
322
|
-
|
321
|
+
;
|
322
|
+
elsif raise_on_unrecognised
|
323
323
|
|
324
|
-
|
324
|
+
if raise_on_unrecognised.is_a?(Class)
|
325
325
|
|
326
|
-
|
327
|
-
|
326
|
+
raise raise_on_unrecognised, message
|
327
|
+
else
|
328
328
|
|
329
|
-
|
330
|
-
|
331
|
-
|
329
|
+
raise RuntimeError, message
|
330
|
+
end
|
331
|
+
elsif climate.exit_on_unknown
|
332
332
|
|
333
|
-
|
334
|
-
|
333
|
+
climate.abort message
|
334
|
+
else
|
335
335
|
|
336
|
-
|
336
|
+
if program_name && !program_name.empty?
|
337
337
|
|
338
|
-
|
339
|
-
|
338
|
+
message = "#{program_name}: #{message}"
|
339
|
+
end
|
340
340
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
341
|
+
climate.stderr.puts message
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
345
|
|
346
|
-
|
346
|
+
# 2. police any required options
|
347
347
|
|
348
|
-
|
348
|
+
climate.check_required_options_(specifications, self.options, [], raise_on_required)
|
349
349
|
|
350
|
-
|
350
|
+
# 3. Check values
|
351
351
|
|
352
|
-
|
353
|
-
|
352
|
+
climate.check_value_constraints_(values)
|
353
|
+
end
|
354
354
|
|
355
|
-
|
355
|
+
def flag_is_specified(id)
|
356
356
|
|
357
|
-
|
358
|
-
|
357
|
+
!@arguments.find_flag(id).nil?
|
358
|
+
end
|
359
359
|
|
360
|
-
|
360
|
+
def lookup_flag(id)
|
361
361
|
|
362
|
-
|
363
|
-
|
362
|
+
@arguments.find_flag(id)
|
363
|
+
end
|
364
364
|
|
365
|
-
|
365
|
+
def lookup_option(id)
|
366
366
|
|
367
|
-
|
368
|
-
|
367
|
+
@arguments.find_option(id)
|
368
|
+
end
|
369
369
|
|
370
|
-
|
370
|
+
end # end class ParseResults
|
371
371
|
|
372
372
|
|
373
|
-
|
373
|
+
#:stopdoc:
|
374
374
|
|
375
|
-
|
376
|
-
|
375
|
+
private
|
376
|
+
module Climate_Constants_
|
377
377
|
|
378
|
-
|
379
|
-
|
378
|
+
GIVEN_SPECS_ = "_Given_Specs_01B59422_8407_4c89_9432_8160C52BD5AD"
|
379
|
+
end # module Climate_Constants_
|
380
380
|
|
381
|
-
|
381
|
+
def make_abort_message_(msg)
|
382
382
|
|
383
|
-
|
383
|
+
if 0 != (usage_help_suffix || 0).size
|
384
384
|
|
385
|
-
|
386
|
-
|
385
|
+
"#{msg}; #{usage_help_suffix}"
|
386
|
+
else
|
387
387
|
|
388
|
-
|
389
|
-
|
390
|
-
|
388
|
+
msg
|
389
|
+
end
|
390
|
+
end
|
391
391
|
|
392
|
-
|
392
|
+
def show_usage_()
|
393
393
|
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
394
|
+
options = {}
|
395
|
+
options.merge! stream: stdout, program_name: program_name, version: version, exit: exit_on_usage ? 0 : nil
|
396
|
+
options[:info_lines] = info_lines if info_lines
|
397
|
+
options[:values] = usage_values if usage_values
|
398
|
+
options[:flags_and_options] = flags_and_options if flags_and_options
|
399
399
|
|
400
|
-
|
401
|
-
|
400
|
+
CLASP.show_usage specifications, options
|
401
|
+
end
|
402
402
|
|
403
|
-
|
403
|
+
def show_version_()
|
404
404
|
|
405
|
-
|
406
|
-
|
405
|
+
CLASP.show_version specifications, stream: stdout, program_name: program_name, version: version, exit: exit_on_usage ? 0 : nil
|
406
|
+
end
|
407
407
|
|
408
|
-
|
408
|
+
def infer_version_(ctxt)
|
409
409
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
410
|
+
# algorithm:
|
411
|
+
#
|
412
|
+
# 1. PROGRAM_VERSION: loaded from ctxt / global
|
413
|
+
# 2. PROGRAM_VER(SION)_(MAJOR|MINOR|(PATCH|REVISION)|BUILD): loaded from
|
414
|
+
# ctxt / global
|
415
415
|
|
416
|
-
|
416
|
+
if ctxt
|
417
417
|
|
418
|
-
|
418
|
+
ctxt = ctxt.class unless ::Class === ctxt
|
419
419
|
|
420
|
-
|
420
|
+
return ctxt.const_get(:PROGRAM_VERSION) if ctxt.const_defined? :PROGRAM_VERSION
|
421
421
|
|
422
|
-
|
422
|
+
ver = []
|
423
423
|
|
424
|
-
|
424
|
+
if ctxt.const_defined? :PROGRAM_VER_MAJOR
|
425
425
|
|
426
|
-
|
426
|
+
ver << ctxt.const_get(:PROGRAM_VER_MAJOR)
|
427
427
|
|
428
|
-
|
428
|
+
if ctxt.const_defined? :PROGRAM_VER_MINOR
|
429
429
|
|
430
|
-
|
430
|
+
ver << ctxt.const_get(:PROGRAM_VER_MINOR)
|
431
431
|
|
432
|
-
|
432
|
+
if ctxt.const_defined?(:PROGRAM_VER_REVISION) || ctxt.const_defined?(:PROGRAM_VER_PATCH)
|
433
433
|
|
434
|
-
|
434
|
+
if ctxt.const_defined?(:PROGRAM_VER_PATCH)
|
435
435
|
|
436
|
-
|
437
|
-
|
436
|
+
ver << ctxt.const_get(:PROGRAM_VER_PATCH)
|
437
|
+
else
|
438
438
|
|
439
|
-
|
440
|
-
|
439
|
+
ver << ctxt.const_get(:PROGRAM_VER_REVISION)
|
440
|
+
end
|
441
441
|
|
442
|
-
|
442
|
+
if ctxt.const_defined? :PROGRAM_VER_BUILD
|
443
443
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
444
|
+
ver << ctxt.const_get(:PROGRAM_VER_BUILD)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
448
|
|
449
|
-
|
450
|
-
|
451
|
-
|
449
|
+
return ver
|
450
|
+
end
|
451
|
+
else
|
452
452
|
|
453
|
-
|
453
|
+
return PROGRAM_VERSION if defined? PROGRAM_VERSION
|
454
454
|
|
455
|
-
|
455
|
+
ver = []
|
456
456
|
|
457
|
-
|
457
|
+
if defined? PROGRAM_VER_MAJOR
|
458
458
|
|
459
|
-
|
459
|
+
ver << PROGRAM_VER_MAJOR
|
460
460
|
|
461
|
-
|
461
|
+
if defined? PROGRAM_VER_MINOR
|
462
462
|
|
463
|
-
|
463
|
+
ver << PROGRAM_VER_MINOR
|
464
464
|
|
465
|
-
|
465
|
+
if defined?(PROGRAM_VER_REVISION) || defined?(PROGRAM_VER_PATCH)
|
466
466
|
|
467
|
-
|
467
|
+
if defined?(PROGRAM_VER_PATCH)
|
468
468
|
|
469
|
-
|
470
|
-
|
469
|
+
ver << PROGRAM_VER_PATCH
|
470
|
+
else
|
471
471
|
|
472
|
-
|
473
|
-
|
472
|
+
ver << PROGRAM_VER_REVISION
|
473
|
+
end
|
474
474
|
|
475
|
-
|
475
|
+
if defined? PROGRAM_VER_BUILD
|
476
476
|
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
477
|
+
ver << PROGRAM_VER_BUILD
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
481
|
|
482
|
-
|
483
|
-
|
484
|
-
|
482
|
+
return ver
|
483
|
+
end
|
484
|
+
end
|
485
485
|
|
486
|
-
|
487
|
-
|
486
|
+
nil
|
487
|
+
end
|
488
488
|
|
489
|
-
|
489
|
+
def self.check_type_(v, types)
|
490
490
|
|
491
|
-
|
491
|
+
return true if v.nil?
|
492
492
|
|
493
|
-
|
493
|
+
types = [ types ] unless Array === types
|
494
494
|
|
495
|
-
|
495
|
+
return true if types.empty?
|
496
496
|
|
497
|
-
|
497
|
+
types.each do |type|
|
498
498
|
|
499
|
-
|
499
|
+
if false
|
500
500
|
|
501
|
-
|
502
|
-
|
501
|
+
;
|
502
|
+
elsif :boolean == type
|
503
503
|
|
504
|
-
|
505
|
-
|
504
|
+
return true if [ TrueClass, FalseClass ].include? v.class
|
505
|
+
elsif type.is_a?(Class)
|
506
506
|
|
507
|
-
|
508
|
-
|
507
|
+
return true if v.is_a?(type)
|
508
|
+
elsif type.is_a?(Array)
|
509
509
|
|
510
|
-
|
510
|
+
t0 = type[0]
|
511
511
|
|
512
|
-
|
512
|
+
if t0
|
513
513
|
|
514
|
-
|
515
|
-
|
514
|
+
#return true if v.is_a?
|
515
|
+
else
|
516
516
|
|
517
|
-
|
517
|
+
# Can be array of anything
|
518
518
|
|
519
|
-
|
520
|
-
|
521
|
-
|
519
|
+
return true if v.is_a?(Array)
|
520
|
+
end
|
521
|
+
else
|
522
522
|
|
523
|
-
|
524
|
-
|
525
|
-
|
523
|
+
warn "Cannot validate type of '#{v}' (#{v.class}) against type specification '#{type}'"
|
524
|
+
end
|
525
|
+
end
|
526
526
|
|
527
|
-
|
528
|
-
|
527
|
+
false
|
528
|
+
end
|
529
529
|
|
530
|
-
|
530
|
+
def self.lookup_element_(h, types, name, path)
|
531
531
|
|
532
|
-
|
532
|
+
if h.has_key?(name)
|
533
533
|
|
534
|
-
|
534
|
+
r = h[name]
|
535
535
|
|
536
|
-
|
536
|
+
unless self.check_type_(r, types)
|
537
537
|
|
538
|
-
|
539
|
-
|
538
|
+
raise TypeError, "element '#{name}' is of type '#{r.class}' and '#{types}' is required"
|
539
|
+
end
|
540
540
|
|
541
|
-
|
542
|
-
|
541
|
+
return r
|
542
|
+
end
|
543
543
|
|
544
|
-
|
545
|
-
|
544
|
+
nil
|
545
|
+
end
|
546
546
|
|
547
|
-
|
547
|
+
def self.require_element_(h, types, name, path)
|
548
548
|
|
549
|
-
|
549
|
+
unless h.has_key?(name)
|
550
550
|
|
551
|
-
|
551
|
+
if (path || '').empty?
|
552
552
|
|
553
|
-
|
554
|
-
|
553
|
+
raise ArgumentError, "missing top-level element '#{name}' in load configuration"
|
554
|
+
else
|
555
555
|
|
556
|
-
|
557
|
-
|
558
|
-
|
556
|
+
raise ArgumentError, "missing element '#{path}/#{name}' in load configuration"
|
557
|
+
end
|
558
|
+
else
|
559
559
|
|
560
|
-
|
560
|
+
r = h[name]
|
561
561
|
|
562
|
-
|
562
|
+
unless self.check_type_(r, types)
|
563
563
|
|
564
|
-
|
565
|
-
|
564
|
+
raise TypeError, "element '#{name}' is of type '#{r.class}' and '#{types}' is required"
|
565
|
+
end
|
566
566
|
|
567
|
-
|
568
|
-
|
569
|
-
|
567
|
+
return r
|
568
|
+
end
|
569
|
+
end
|
570
570
|
|
571
|
-
|
572
|
-
|
571
|
+
public
|
572
|
+
def check_required_options_(specifications, options, missing, raise_on_required)
|
573
573
|
|
574
|
-
|
574
|
+
required_specifications = specifications.select do |sp|
|
575
575
|
|
576
|
-
|
577
|
-
|
576
|
+
sp.kind_of?(::CLASP::OptionSpecification) && sp.required?
|
577
|
+
end
|
578
578
|
|
579
|
-
|
579
|
+
required_specifications = Hash[required_specifications.map { |sp| [ sp.name, sp ] }]
|
580
580
|
|
581
|
-
|
581
|
+
given_options = Hash[options.map { |o| [ o.name, o ]}]
|
582
582
|
|
583
|
-
|
583
|
+
required_specifications.each do |k, sp|
|
584
584
|
|
585
|
-
|
585
|
+
unless given_options.has_key? k
|
586
586
|
|
587
|
-
|
587
|
+
message = sp.required_message
|
588
588
|
|
589
|
-
|
589
|
+
if false
|
590
590
|
|
591
|
-
|
592
|
-
|
591
|
+
;
|
592
|
+
elsif raise_on_required
|
593
593
|
|
594
|
-
|
594
|
+
if raise_on_required.is_a?(Class)
|
595
595
|
|
596
|
-
|
597
|
-
|
596
|
+
raise raise_on_required, message
|
597
|
+
else
|
598
598
|
|
599
|
-
|
600
|
-
|
601
|
-
|
599
|
+
raise RuntimeError, message
|
600
|
+
end
|
601
|
+
elsif exit_on_missing
|
602
602
|
|
603
|
-
|
604
|
-
|
603
|
+
self.abort message
|
604
|
+
else
|
605
605
|
|
606
|
-
|
606
|
+
if program_name && !program_name.empty?
|
607
607
|
|
608
|
-
|
609
|
-
|
608
|
+
message = "#{program_name}: #{message}"
|
609
|
+
end
|
610
610
|
|
611
|
-
|
612
|
-
|
611
|
+
stderr.puts message
|
612
|
+
end
|
613
613
|
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
614
|
+
missing << sp
|
615
|
+
#results[:missing_option_aliases] << sp
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
619
619
|
|
620
|
-
|
620
|
+
def check_value_constraints_(values)
|
621
621
|
|
622
|
-
|
622
|
+
# now police the values
|
623
623
|
|
624
|
-
|
625
|
-
|
626
|
-
|
624
|
+
values_constraint = constrain_values
|
625
|
+
values_constraint = values_constraint.begin if ::Range === values_constraint && values_constraint.end == values_constraint.begin
|
626
|
+
val_names = ::Array === value_names ? value_names : []
|
627
627
|
|
628
|
-
|
629
|
-
|
628
|
+
case values_constraint
|
629
|
+
when nil
|
630
630
|
|
631
|
-
|
632
|
-
|
631
|
+
;
|
632
|
+
when ::Array
|
633
633
|
|
634
|
-
|
634
|
+
warn "value of 'constrain_values' attribute, if an #{::Array}, must not be empty and all elements must be of type #{::Integer}" if values_constraint.empty? || !values_constraint.all? { |v| ::Integer === v }
|
635
635
|
|
636
|
-
|
636
|
+
unless values_constraint.include? values.size
|
637
637
|
|
638
|
-
|
638
|
+
message = make_abort_message_("wrong number of values: #{values.size} given, #{values_constraint} required")
|
639
639
|
|
640
|
-
|
640
|
+
if exit_on_missing
|
641
641
|
|
642
|
-
|
643
|
-
|
642
|
+
self.abort message
|
643
|
+
else
|
644
644
|
|
645
|
-
|
645
|
+
if program_name && !program_name.empty?
|
646
646
|
|
647
|
-
|
648
|
-
|
647
|
+
message = "#{program_name}: #{message}"
|
648
|
+
end
|
649
649
|
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
650
|
+
stderr.puts message
|
651
|
+
end
|
652
|
+
end
|
653
|
+
when ::Integer
|
654
654
|
|
655
|
-
|
655
|
+
unless values.size == values_constraint
|
656
656
|
|
657
|
-
|
657
|
+
if name = val_names[values.size]
|
658
658
|
|
659
|
-
|
660
|
-
|
659
|
+
message = make_abort_message_(name + ' not specified')
|
660
|
+
else
|
661
661
|
|
662
|
-
|
663
|
-
|
662
|
+
message = make_abort_message_("wrong number of values: #{values.size} given, #{values_constraint} required")
|
663
|
+
end
|
664
664
|
|
665
|
-
|
665
|
+
if exit_on_missing
|
666
666
|
|
667
|
-
|
668
|
-
|
667
|
+
self.abort message
|
668
|
+
else
|
669
669
|
|
670
|
-
|
670
|
+
if program_name && !program_name.empty?
|
671
671
|
|
672
|
-
|
673
|
-
|
672
|
+
message = "#{program_name}: #{message}"
|
673
|
+
end
|
674
674
|
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
675
|
+
stderr.puts message
|
676
|
+
end
|
677
|
+
end
|
678
|
+
when ::Range
|
679
679
|
|
680
|
-
|
680
|
+
unless values_constraint.include? values.size
|
681
681
|
|
682
|
-
|
682
|
+
if name = val_names[values.size]
|
683
683
|
|
684
|
-
|
685
|
-
|
684
|
+
message = make_abort_message_(name + ' not specified')
|
685
|
+
else
|
686
686
|
|
687
|
-
|
688
|
-
|
687
|
+
message = make_abort_message_("wrong number of values: #{values.size} givens, #{values_constraint.begin} - #{values_constraint.end - (values_constraint.exclude_end? ? 1 : 0)} required")
|
688
|
+
end
|
689
689
|
|
690
|
-
|
690
|
+
if exit_on_missing
|
691
691
|
|
692
|
-
|
693
|
-
|
692
|
+
self.abort message
|
693
|
+
else
|
694
694
|
|
695
|
-
|
695
|
+
if program_name && !program_name.empty?
|
696
696
|
|
697
|
-
|
698
|
-
|
697
|
+
message = "#{program_name}: #{message}"
|
698
|
+
end
|
699
699
|
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
700
|
+
stderr.puts message
|
701
|
+
end
|
702
|
+
end
|
703
|
+
else
|
704
704
|
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
705
|
+
warn "value of 'constrain_values' attribute - '#{constrain_values}' (#{constrain_values.class}) - of wrong type : must be #{::Array}, #{::Integer}, #{::Range}, or nil"
|
706
|
+
end
|
707
|
+
end
|
708
|
+
#:startdoc:
|
709
709
|
|
710
|
-
|
710
|
+
public
|
711
711
|
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
712
|
+
# Loads an instance of the class, as specified by +source+, according to the given parameters
|
713
|
+
#
|
714
|
+
# === Signature
|
715
|
+
#
|
716
|
+
# * *Parameters:*
|
717
|
+
# - +source+:: (+Hash+, +IO+) The arguments specification, either as a Hash or an instance of an IO-implementing type containing a YAML specification
|
718
|
+
# - +options+:: An options hash, containing any of the following options
|
719
|
+
#
|
720
|
+
# * *Options:*
|
721
|
+
# - +:no_help_flag+ (boolean) Prevents the use of the +CLASP::Flag.Help+ flag-specification
|
722
|
+
# - +:no_version_flag+ (boolean) Prevents the use of the +CLASP::Flag.Version+ flag-specification
|
723
|
+
# - +:program_name+ (::String) An explicit program-name, which is inferred from +$0+ if this is +nil+
|
724
|
+
# - +:version+ (String, [Integer], [String]) A version specification. If not specified, this is inferred
|
725
|
+
# - +:version_context+ Object or class that defines a context for searching the version. Ignored if +:version+ is specified
|
726
|
+
#
|
727
|
+
# * *Block* An optional block that receives the initialising Climate instance, allowing the user to modify the attributes.
|
728
|
+
def self.load(source, options = (options_defaulted_ = {}), &blk) # :yields: climate
|
729
729
|
|
730
|
-
|
730
|
+
check_parameter options, 'options', allow_nil: true, type: ::Hash
|
731
731
|
|
732
|
-
|
732
|
+
options ||= {}
|
733
733
|
|
734
|
-
|
734
|
+
h = nil
|
735
735
|
|
736
|
-
|
737
|
-
|
736
|
+
case source
|
737
|
+
when ::IO
|
738
738
|
|
739
|
-
|
740
|
-
|
739
|
+
h = YAML.load source.read
|
740
|
+
when ::Hash
|
741
741
|
|
742
|
-
|
743
|
-
|
742
|
+
h = source
|
743
|
+
else
|
744
744
|
|
745
|
-
|
745
|
+
if source.respond_to?(:to_hash)
|
746
746
|
|
747
|
-
|
748
|
-
|
747
|
+
h = source.to_hash
|
748
|
+
else
|
749
749
|
|
750
|
-
|
751
|
-
|
752
|
-
|
750
|
+
raise TypeError, "#{self}.#{__method__}() 'source' argument must be a #{::Hash}, or an object implementing #{::IO}, or a type implementing 'to_hash'"
|
751
|
+
end
|
752
|
+
end
|
753
753
|
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
754
|
+
_libclimate = require_element_(h, Hash, 'libclimate', nil)
|
755
|
+
_exit_on_missing = lookup_element_(_libclimate, :boolean, 'exit_on_missing', 'libclimate')
|
756
|
+
_ignore_unknown = lookup_element_(_libclimate, :boolean, 'ignore_unknown', 'libclimate')
|
757
|
+
_exit_on_unknown = lookup_element_(_libclimate, :boolean, 'exit_on_unknown', 'libclimate')
|
758
|
+
_exit_on_usage = lookup_element_(_libclimate, :boolean, 'exit_on_usage', 'libclimate')
|
759
|
+
_info_lines = lookup_element_(_libclimate, Array, 'info_lines', 'libclimate')
|
760
|
+
_program_name = lookup_element_(_libclimate, String, 'program_name', 'libclimate')
|
761
|
+
_constrain_values = lookup_element_(_libclimate, [ Integer, Range ], 'constrain_values', 'libclimate')
|
762
|
+
_flags_and_options = lookup_element_(_libclimate, String, 'flags_and_options', 'libclimate')
|
763
|
+
_usage_values = lookup_element_(_libclimate, String, 'usage_values', 'libclimate')
|
764
|
+
_value_names = lookup_element_(_libclimate, Array, 'value_names', 'libclimate')
|
765
|
+
_version = lookup_element_(_libclimate, [ String, [] ], 'version', 'libclimate')
|
766
766
|
|
767
|
-
|
767
|
+
specs = CLASP::Arguments.load_specifications _libclimate, options
|
768
768
|
|
769
|
-
|
769
|
+
cl = Climate.new(options.merge(Climate_Constants_::GIVEN_SPECS_ => specs), &blk)
|
770
770
|
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
771
|
+
cl.exit_on_missing = _exit_on_missing unless _exit_on_missing.nil?
|
772
|
+
cl.ignore_unknown = _ignore_unknown unless _ignore_unknown.nil?
|
773
|
+
cl.exit_on_unknown = _exit_on_unknown unless _exit_on_unknown.nil?
|
774
|
+
cl.exit_on_usage = _exit_on_usage unless _exit_on_usage.nil?
|
775
|
+
cl.info_lines = _info_lines unless _info_lines.nil?
|
776
|
+
cl.program_name = _program_name unless _program_name.nil?
|
777
|
+
cl.constrain_values = _constrain_values unless _constrain_values.nil?
|
778
|
+
cl.flags_and_options = _flags_and_options unless _flags_and_options.nil?
|
779
|
+
cl.usage_values = _usage_values unless _usage_values.nil?
|
780
|
+
cl.value_names = _value_names unless _value_names.nil?
|
781
|
+
cl.version = _version unless _version.nil?
|
782
782
|
|
783
|
-
|
784
|
-
|
783
|
+
cl
|
784
|
+
end
|
785
785
|
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
786
|
+
# Creates an instance of the Climate class.
|
787
|
+
#
|
788
|
+
# === Signature
|
789
|
+
#
|
790
|
+
# * *Parameters:*
|
791
|
+
# - +options:+ (Hash) An options hash, containing any of the following options.
|
792
|
+
#
|
793
|
+
# * *Options:*
|
794
|
+
# - +:no_help_flag+ (boolean) Prevents the use of the +CLASP::Flag.Help+ flag-specification
|
795
|
+
# - +:no_version_flag+ (boolean) Prevents the use of the +CLASP::Flag.Version+ flag-specification
|
796
|
+
# - +:program_name+ (::String) An explicit program-name, which is inferred from +$0+ if this is +nil+
|
797
|
+
# - +:value_attributes+ (boolean) Causes any possible value-names, as described in `#value_names`, to be applied as attributes with the given values, if any, on the command-line
|
798
|
+
# - +:version+ (String, [Integer], [String]) A version specification. If not specified, this is inferred
|
799
|
+
# - +:version_context+ Object or class that defines a context for searching the version. Ignored if +:version+ is specified
|
800
|
+
#
|
801
|
+
# * *Block* An optional block that receives the initialising Climate instance, allowing the user to modify the attributes.
|
802
|
+
def initialize(options={}, &blk) # :yields: climate
|
803
|
+
|
804
|
+
check_parameter options, 'options', allow_nil: true, type: ::Hash
|
805
|
+
|
806
|
+
options ||= {}
|
807
|
+
|
808
|
+
check_option options, :no_help_flag, type: :boolean, allow_nil: true
|
809
|
+
check_option options, :no_version_flag, type: :boolean, allow_nil: true
|
810
|
+
check_option options, :program_name, type: ::String, allow_nil: true
|
811
|
+
|
812
|
+
pr_name = options[:program_name]
|
813
|
+
|
814
|
+
unless pr_name
|
815
|
+
|
816
|
+
pr_name = File.basename($0)
|
817
|
+
pr_name = (pr_name =~ /\.(?:bat|cmd|rb|sh)$/) ? "#$`(#$&)" : pr_name
|
818
|
+
end
|
819
|
+
|
820
|
+
given_specs = options[Climate_Constants_::GIVEN_SPECS_]
|
821
|
+
|
822
|
+
@specifications = []
|
823
|
+
@ignore_unknown = false
|
824
|
+
@exit_on_unknown = true
|
825
|
+
@exit_on_missing = true
|
826
|
+
@exit_on_usage = true
|
827
|
+
@info_lines = nil
|
828
|
+
set_program_name pr_name
|
829
|
+
@stdout = $stdout
|
830
|
+
@stderr = $stderr
|
831
|
+
@constrain_values = nil
|
832
|
+
@flags_and_options = flags_and_options
|
833
|
+
@usage_help_suffix = 'use --help for usage'
|
834
|
+
@usage_values = usage_values
|
835
|
+
@value_names = []
|
836
|
+
version_context = options[:version_context]
|
837
|
+
@version = options[:version] || infer_version_(version_context)
|
838
|
+
|
839
|
+
@value_attributes = options[:value_attributes]
|
840
|
+
|
841
|
+
unless options[:no_help_flag]
|
842
|
+
|
843
|
+
f = CLASP::Flag.Help()
|
844
|
+
|
845
|
+
unless f.respond_to?(:action)
|
846
|
+
|
847
|
+
class << f
|
848
|
+
|
849
|
+
attr_accessor :action
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
f.action = Proc.new { show_usage_ }
|
854
|
+
|
855
|
+
@specifications << f
|
856
|
+
end
|
857
|
+
|
858
|
+
unless options[:no_version_flag]
|
859
|
+
|
860
|
+
f = CLASP::Flag.Version()
|
861
|
+
|
862
|
+
unless f.respond_to?(:action)
|
863
|
+
|
864
|
+
class << f
|
865
|
+
|
866
|
+
attr_accessor :action
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
f.action = Proc.new { show_version_ }
|
871
|
+
|
872
|
+
@specifications << f
|
873
|
+
end
|
874
|
+
|
875
|
+
@specifications = @specifications + given_specs if given_specs
|
876
|
+
|
877
|
+
yield self if block_given?
|
878
|
+
end
|
879
|
+
|
880
|
+
# [DEPRECATED] This method is now deprecated. Instead use +program_name=+
|
881
|
+
def set_program_name(name)
|
882
|
+
|
883
|
+
@program_name = name
|
884
|
+
end
|
885
|
+
|
886
|
+
# ([CLASP::Specification]) An array of specifications attached to the climate instance, whose contents should be modified by adding (or removing) CLASP specifications
|
887
|
+
attr_reader :specifications
|
888
|
+
# [DEPRECATED] Instead, use +specifications+
|
889
|
+
def aliases; specifications; end
|
890
|
+
# (boolean) Indicates whether exit will be called (with non-zero exit code) when a required command-line option is missing. Defaults to +true+
|
891
|
+
attr_accessor :exit_on_missing
|
892
|
+
# (boolean) Indicates whether unknown flags or options will be ignored. This overrides +:exit_on_unknown+. Defaults to +false+
|
893
|
+
attr_accessor :ignore_unknown
|
894
|
+
# (boolean) Indicates whether exit will be called (with non-zero exit code) when an unknown command-line flag or option is encountered. Defaults to +true+
|
895
|
+
attr_accessor :exit_on_unknown
|
896
|
+
# (boolean) Indicates whether exit will be called (with zero exit code) when usage/version is requested on the command-line. Defaults to +true+
|
897
|
+
attr_accessor :exit_on_usage
|
898
|
+
# ([String]) Optional array of string of program-information that will be written before the rest of the usage block when usage is requested on the command-line
|
899
|
+
attr_accessor :info_lines
|
900
|
+
# (String) A program name; defaults to the name of the executing script
|
901
|
+
def program_name
|
902
|
+
|
903
|
+
name = @program_name
|
904
|
+
|
905
|
+
if defined?(Colcon) && @stdout.tty?
|
906
|
+
|
907
|
+
name = "#{::Colcon::Decorations::Bold}#{name}#{::Colcon::Decorations::Unbold}"
|
908
|
+
end
|
909
|
+
|
910
|
+
name
|
911
|
+
end
|
912
|
+
# Sets the +program_name+ attribute
|
913
|
+
attr_writer :program_name
|
914
|
+
# @return (::IO) The output stream for normative output; defaults to $stdout
|
915
|
+
attr_accessor :stdout
|
916
|
+
# @return (::IO) The output stream for contingent output; defaults to $stderr
|
917
|
+
attr_accessor :stderr
|
918
|
+
# (Integer, Range) Optional constraint on the values that must be provided to the program
|
919
|
+
attr_accessor :constrain_values
|
920
|
+
# (String) Optional string to describe the flags and options section. Defaults to "[ +...+ +flags+ +and+ +options+ +...+ ]"
|
921
|
+
attr_accessor :flags_and_options
|
922
|
+
# (String) The string that is appended to #abort calls made during #run. Defaults to "use --help for usage"
|
923
|
+
attr_accessor :usage_help_suffix
|
924
|
+
# @return (::String) Optional string to describe the program values, eg \<xyz "[ { <<directory> | <file> } ]"
|
925
|
+
attr_accessor :usage_values
|
926
|
+
# ([String]) Zero-based array of names for values to be used when that value is not present (according to the +:constrain_values+ attribute)
|
927
|
+
attr_accessor :value_names
|
928
|
+
# (String, [String], [Integer]) A version string or an array of integers/strings representing the version component(s)
|
929
|
+
attr_accessor :version
|
930
|
+
|
931
|
+
# Parse the given command-line (passed as +argv+) by the given instance
|
932
|
+
#
|
933
|
+
# === Signature
|
934
|
+
#
|
935
|
+
# * *Parameters:*
|
936
|
+
# - +argv+ ([String]) The array of arguments; defaults to <tt>ARGV</tt>
|
937
|
+
#
|
938
|
+
# === Returns
|
939
|
+
# (ParseResults) Results
|
940
|
+
def parse(argv = ARGV, **options) # :yields: ParseResults
|
941
|
+
|
942
|
+
raise ArgumentError, "argv may not be nil" if argv.nil?
|
943
|
+
|
944
|
+
arguments = CLASP::Arguments.new argv, specifications
|
945
|
+
|
946
|
+
ParseResults.new(self, arguments, argv)
|
947
|
+
end
|
948
|
+
|
949
|
+
# Parse the given command-line (passed as +argv+) by the given instance,
|
950
|
+
# and verifies it
|
951
|
+
#
|
952
|
+
# === Signature
|
953
|
+
#
|
954
|
+
# * *Parameters:*
|
955
|
+
# - +argv+ ([String]) The array of arguments; defaults to <tt>ARGV</tt>
|
956
|
+
# - +options+ (Hash) Options
|
957
|
+
#
|
958
|
+
# * *Options:*
|
959
|
+
# - +:raise_on_required+ (boolean, Exception) Causes an/the given exception to be raised if any required options are not specified in the command-line
|
960
|
+
# - +:raise_on_unrecognised+ (boolean, Exception) Causes an/the given exception to be raised if any unrecognised flags/options are specified in the command-line
|
961
|
+
# - +:raise_on_unused+ (boolean, Exception) Causes an/the given exception to be raised if any given flags/options are not used
|
962
|
+
# - +:raise+ (boolean, Exception) Causes an/the given exception to be raised in all conditions
|
963
|
+
#
|
964
|
+
# === Returns
|
965
|
+
# (ParseResults) Results
|
966
|
+
def parse_and_verify(argv = ARGV, **options) # :yields: ParseResults
|
967
|
+
|
968
|
+
r = parse argv, **options
|
969
|
+
|
970
|
+
r.verify(**options)
|
971
|
+
|
972
|
+
r
|
973
|
+
end
|
974
|
+
|
975
|
+
# [DEPRECATED] Use +Climate#parse_and_verify()+ (but be aware that the
|
976
|
+
# returned result is of a different type
|
977
|
+
#
|
978
|
+
# === Signature
|
979
|
+
#
|
980
|
+
# * *Parameters:*
|
981
|
+
# - +argv+ ([String]) The array of arguments; defaults to <tt>ARGV</tt>
|
982
|
+
#
|
983
|
+
# === Returns
|
984
|
+
# an instance of a type derived from +::Hash+ with the additional
|
985
|
+
# attributes +flags+, +options+, +values+, +double_slash_index+ and
|
986
|
+
# +argv+.
|
987
|
+
def run(argv = ARGV, **options) # :yields: customised +::Hash+
|
988
988
|
|
989
|
-
|
989
|
+
raise ArgumentError, "argv may not be nil" if argv.nil?
|
990
990
|
|
991
|
-
|
991
|
+
arguments = CLASP::Arguments.new argv, specifications
|
992
992
|
|
993
|
-
|
994
|
-
|
993
|
+
run_ argv, arguments, **options
|
994
|
+
end
|
995
995
|
|
996
|
-
|
997
|
-
|
996
|
+
private
|
997
|
+
def run_(argv, arguments, **opts) # :nodoc:
|
998
998
|
|
999
|
-
|
1000
|
-
|
1001
|
-
|
999
|
+
flags = arguments.flags
|
1000
|
+
options = arguments.options
|
1001
|
+
values = arguments.values.to_a
|
1002
1002
|
|
1003
|
-
|
1003
|
+
double_slash_index = arguments.double_slash_index
|
1004
1004
|
|
1005
|
-
|
1005
|
+
results = {
|
1006
1006
|
|
1007
|
-
|
1007
|
+
flags: {
|
1008
1008
|
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1009
|
+
given: flags,
|
1010
|
+
handled: [],
|
1011
|
+
unhandled: [],
|
1012
|
+
unknown: [],
|
1013
|
+
},
|
1014
1014
|
|
1015
|
-
|
1015
|
+
options: {
|
1016
1016
|
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1017
|
+
given: options,
|
1018
|
+
handled: [],
|
1019
|
+
unhandled: [],
|
1020
|
+
unknown: [],
|
1021
|
+
},
|
1022
1022
|
|
1023
|
-
|
1023
|
+
values: values,
|
1024
1024
|
|
1025
|
-
|
1026
|
-
|
1025
|
+
missing_option_aliases: [],
|
1026
|
+
}
|
1027
1027
|
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1028
|
+
# Verification:
|
1029
|
+
#
|
1030
|
+
# 1. Check arguments recognised
|
1031
|
+
# 1.a Flags
|
1032
|
+
# 1.b Options
|
1033
|
+
# 2. police any required options
|
1034
|
+
# 3. Check values
|
1035
1035
|
|
1036
|
-
|
1036
|
+
# 1.a Flags
|
1037
1037
|
|
1038
|
-
|
1038
|
+
flags.each do |f|
|
1039
1039
|
|
1040
|
-
|
1040
|
+
spec = specifications.detect do |sp|
|
1041
1041
|
|
1042
|
-
|
1043
|
-
|
1042
|
+
sp.kind_of?(::CLASP::FlagSpecification) && f.name == sp.name
|
1043
|
+
end
|
1044
1044
|
|
1045
|
-
|
1045
|
+
if spec
|
1046
1046
|
|
1047
|
-
|
1047
|
+
selector = :unhandled
|
1048
1048
|
|
1049
|
-
|
1049
|
+
if spec.respond_to?(:action) && !spec.action.nil?
|
1050
1050
|
|
1051
|
-
|
1051
|
+
spec.action.call(f, spec)
|
1052
1052
|
|
1053
|
-
|
1054
|
-
|
1053
|
+
selector = :handled
|
1054
|
+
end
|
1055
1055
|
|
1056
|
-
|
1057
|
-
|
1056
|
+
results[:flags][selector] << f
|
1057
|
+
else
|
1058
1058
|
|
1059
|
-
|
1059
|
+
message = make_abort_message_("unrecognised flag '#{f}'")
|
1060
1060
|
|
1061
|
-
|
1061
|
+
if false
|
1062
1062
|
|
1063
|
-
|
1063
|
+
elsif ignore_unknown
|
1064
1064
|
|
1065
|
-
|
1066
|
-
|
1065
|
+
;
|
1066
|
+
elsif exit_on_unknown
|
1067
1067
|
|
1068
|
-
|
1069
|
-
|
1068
|
+
self.abort message
|
1069
|
+
else
|
1070
1070
|
|
1071
|
-
|
1071
|
+
if program_name && !program_name.empty?
|
1072
1072
|
|
1073
|
-
|
1074
|
-
|
1073
|
+
message = "#{program_name}: #{message}"
|
1074
|
+
end
|
1075
1075
|
|
1076
|
-
|
1077
|
-
|
1076
|
+
stderr.puts message
|
1077
|
+
end
|
1078
1078
|
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1079
|
+
results[:flags][:unknown] << f
|
1080
|
+
end
|
1081
|
+
end
|
1082
1082
|
|
1083
|
-
|
1083
|
+
# 1.b Options
|
1084
1084
|
|
1085
|
-
|
1085
|
+
options.each do |o|
|
1086
1086
|
|
1087
|
-
|
1087
|
+
spec = specifications.detect do |sp|
|
1088
1088
|
|
1089
|
-
|
1090
|
-
|
1089
|
+
sp.kind_of?(::CLASP::OptionSpecification) && o.name == sp.name
|
1090
|
+
end
|
1091
1091
|
|
1092
|
-
|
1092
|
+
if spec
|
1093
1093
|
|
1094
|
-
|
1094
|
+
selector = :unhandled
|
1095
1095
|
|
1096
|
-
|
1096
|
+
if spec.respond_to?(:action) && !spec.action.nil?
|
1097
1097
|
|
1098
|
-
|
1098
|
+
spec.action.call(o, spec)
|
1099
1099
|
|
1100
|
-
|
1101
|
-
|
1100
|
+
selector = :handled
|
1101
|
+
end
|
1102
1102
|
|
1103
|
-
|
1104
|
-
|
1103
|
+
results[:options][selector] << o
|
1104
|
+
else
|
1105
1105
|
|
1106
|
-
|
1106
|
+
message = make_abort_message_("unrecognised option '#{o}'")
|
1107
1107
|
|
1108
|
-
|
1108
|
+
if false
|
1109
1109
|
|
1110
|
-
|
1110
|
+
elsif ignore_unknown
|
1111
1111
|
|
1112
|
-
|
1113
|
-
|
1112
|
+
;
|
1113
|
+
elsif exit_on_unknown
|
1114
1114
|
|
1115
|
-
|
1116
|
-
|
1115
|
+
self.abort message
|
1116
|
+
else
|
1117
1117
|
|
1118
|
-
|
1118
|
+
if program_name && !program_name.empty?
|
1119
1119
|
|
1120
|
-
|
1121
|
-
|
1120
|
+
message = "#{program_name}: #{message}"
|
1121
|
+
end
|
1122
1122
|
|
1123
|
-
|
1124
|
-
|
1123
|
+
stderr.puts message
|
1124
|
+
end
|
1125
1125
|
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1126
|
+
results[:options][:unknown] << o
|
1127
|
+
end
|
1128
|
+
end
|
1129
1129
|
|
1130
|
-
|
1130
|
+
# 2. police any required options
|
1131
1131
|
|
1132
|
-
|
1132
|
+
check_required_options_(specifications, results[:options][:given], results[:missing_option_aliases], false)
|
1133
1133
|
|
1134
|
-
|
1134
|
+
# 3. Check values
|
1135
1135
|
|
1136
|
-
|
1136
|
+
check_value_constraints_(values)
|
1137
1137
|
|
1138
1138
|
|
1139
1139
|
|
1140
|
-
|
1140
|
+
def results.flags
|
1141
1141
|
|
1142
|
-
|
1143
|
-
|
1142
|
+
self[:flags]
|
1143
|
+
end
|
1144
1144
|
|
1145
|
-
|
1145
|
+
def results.options
|
1146
1146
|
|
1147
|
-
|
1148
|
-
|
1147
|
+
self[:options]
|
1148
|
+
end
|
1149
1149
|
|
1150
|
-
|
1150
|
+
def results.values
|
1151
1151
|
|
1152
|
-
|
1153
|
-
|
1152
|
+
self[:values]
|
1153
|
+
end
|
1154
1154
|
|
1155
|
-
|
1155
|
+
results.define_singleton_method(:double_slash_index) do
|
1156
1156
|
|
1157
|
-
|
1158
|
-
|
1157
|
+
double_slash_index
|
1158
|
+
end
|
1159
1159
|
|
1160
|
-
|
1160
|
+
results.define_singleton_method(:argv) do
|
1161
1161
|
|
1162
|
-
|
1163
|
-
|
1162
|
+
argv
|
1163
|
+
end
|
1164
1164
|
|
1165
1165
|
|
1166
|
-
|
1166
|
+
if opts[:value_attributes] || @value_attributes
|
1167
1167
|
|
1168
|
-
|
1168
|
+
(value_names || []).each_with_index do |name, index|
|
1169
1169
|
|
1170
|
-
|
1170
|
+
name = name.gsub(/-/, '_').to_sym
|
1171
1171
|
|
1172
|
-
|
1172
|
+
if results.respond_to? name
|
1173
1173
|
|
1174
|
-
|
1175
|
-
|
1174
|
+
warn "cannot create attribute `#{name}` on instance of `#{results.class}` because that name is already used"
|
1175
|
+
else
|
1176
1176
|
|
1177
|
-
|
1177
|
+
value = results.values[index]
|
1178
1178
|
|
1179
|
-
|
1179
|
+
results.define_singleton_method(name) do
|
1180
1180
|
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1181
|
+
value
|
1182
|
+
end
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
end
|
1186
1186
|
|
1187
1187
|
|
1188
|
-
|
1188
|
+
results.freeze
|
1189
1189
|
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1190
|
+
results
|
1191
|
+
end
|
1192
|
+
public
|
1193
1193
|
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1194
|
+
# Calls abort() with the given message prefixed by the program_name
|
1195
|
+
#
|
1196
|
+
# === Signature
|
1197
|
+
#
|
1198
|
+
# * *Parameters:*
|
1199
|
+
# - +message+ (String) The message string
|
1200
|
+
# - +options+ (Hash) An option hash, containing any of the following options
|
1201
|
+
#
|
1202
|
+
# * *Options:*
|
1203
|
+
# - +:stream+ {optional} The output stream to use. Defaults to the value of the attribute +stderr+
|
1204
|
+
# - +:program_name+ (String) {optional} Uses the given value rather than the +program_name+ attribute; does not prefix if the empty string
|
1205
|
+
# - +:exit+ {optional} The exit code. Defaults to 1. Does not exit if +nil+ specified explicitly
|
1206
|
+
#
|
1207
|
+
# === Returns
|
1208
|
+
# The combined message string, if <tt>exit()</tt> not called.
|
1209
|
+
def abort(message, options={})
|
1210
1210
|
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1211
|
+
prog_name = options[:program_name]
|
1212
|
+
prog_name ||= program_name
|
1213
|
+
prog_name ||= ''
|
1214
1214
|
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1215
|
+
stream = options[:stream]
|
1216
|
+
stream ||= stderr
|
1217
|
+
stream ||= $stderr
|
1218
1218
|
|
1219
|
-
|
1219
|
+
exit_code = options.has_key?(:exit) ? options[:exit] : 1
|
1220
1220
|
|
1221
|
-
|
1221
|
+
if prog_name.empty?
|
1222
1222
|
|
1223
|
-
|
1224
|
-
|
1223
|
+
msg = message
|
1224
|
+
else
|
1225
1225
|
|
1226
|
-
|
1227
|
-
|
1226
|
+
msg = "#{prog_name}: #{message}"
|
1227
|
+
end
|
1228
1228
|
|
1229
1229
|
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1230
|
+
stream.puts msg
|
1231
|
+
|
1232
|
+
exit(exit_code) if exit_code
|
1233
|
+
|
1234
|
+
msg
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
# Adds a flag to +specifications+
|
1238
|
+
#
|
1239
|
+
# === Signature
|
1240
|
+
#
|
1241
|
+
# * *Parameters:*
|
1242
|
+
# - +name_or_flag+ (String, ::CLASP::FlagSpecification) The flag name or instance of CLASP::FlagSpecification
|
1243
|
+
# - +options+ (Hash) An options hash, containing any of the following options
|
1244
|
+
#
|
1245
|
+
# * *Options:*
|
1246
|
+
# - +:alias+ (String) A single alias
|
1247
|
+
# - +:aliases+ ([String]) An array of aliases
|
1248
|
+
# - +:help+ (String) Description string used when writing response to "+--help+" flag
|
1249
|
+
# - +:required+ (boolean) Indicates whether the flag is required, causing #run to fail with appropriate message if the flag is not specified in the command-line arguments
|
1250
|
+
#
|
1251
|
+
# * *Block* An optional block that is invoked when the parsed command-line contains the given flag, receiving the argument and the alias
|
1252
|
+
#
|
1253
|
+
# === Examples
|
1254
|
+
#
|
1255
|
+
# ==== Specification(s) of a flag (single statement)
|
1256
|
+
#
|
1257
|
+
def add_flag(name_or_flag, options={}, &blk)
|
1258
|
+
|
1259
|
+
check_parameter name_or_flag, 'name_or_flag', allow_nil: false, types: [ ::String, ::Symbol, ::CLASP::FlagSpecification ]
|
1260
|
+
|
1261
|
+
if ::CLASP::FlagSpecification === name_or_flag
|
1262
|
+
|
1263
|
+
specifications << name_or_flag
|
1264
|
+
else
|
1265
|
+
|
1266
|
+
specifications << CLASP.Flag(name_or_flag, **options, &blk)
|
1267
|
+
end
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
# Adds an option to +specifications+
|
1271
|
+
#
|
1272
|
+
# === Signature
|
1273
|
+
#
|
1274
|
+
# * *Parameters:*
|
1275
|
+
# - +name_or_option+ (String, CLASP::OptionSpecification) The option name or instance of CLASP::OptionSpecification
|
1276
|
+
# - +options+ (Hash) An options hash, containing any of the following options
|
1277
|
+
#
|
1278
|
+
# * *Options:*
|
1279
|
+
# - +:alias+ (String) A single alias
|
1280
|
+
# - +:aliases+ ([String]) An array of aliases
|
1281
|
+
# - +:help+ (String) Description string used when writing response to "+--help+" flag
|
1282
|
+
# - +:values_range+ ([String]) An array of strings representing the valid/expected values used when writing response to "+--help+" flag. NOTE: the current version does not validate against these values, but a future version may do so
|
1283
|
+
# - +:default_value+ (String) The default version used when, say, for the option +--my-opt+ the command-line contain the argument "+--my-opt=+"
|
1284
|
+
#
|
1285
|
+
# * *Block* An optional block that is invoked when the parsed command-line contains the given option, receiving the argument and the alias
|
1286
|
+
#
|
1287
|
+
def add_option(name_or_option, options={}, &blk)
|
1288
|
+
|
1289
|
+
check_parameter name_or_option, 'name_or_option', allow_nil: false, types: [ ::String, ::Symbol, ::CLASP::OptionSpecification ]
|
1290
|
+
|
1291
|
+
if ::CLASP::OptionSpecification === name_or_option
|
1292
|
+
|
1293
|
+
specifications << name_or_option
|
1294
|
+
else
|
1295
|
+
|
1296
|
+
specifications << CLASP.Option(name_or_option, **options, &blk)
|
1297
|
+
end
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
# Adds an alias to +specifications+
|
1301
|
+
#
|
1302
|
+
# === Signature
|
1303
|
+
#
|
1304
|
+
# * *Parameters:*
|
1305
|
+
# - +name_or_specification+ (String) The flag/option name or the valued option
|
1306
|
+
# - +aliases+ (*[String]) One or more aliases
|
1307
|
+
#
|
1308
|
+
# === Examples
|
1309
|
+
#
|
1310
|
+
# ==== Specification(s) of a flag (single statement)
|
1311
|
+
#
|
1312
|
+
# climate.add_flag('--mark-missing', alias: '-x')
|
1313
|
+
#
|
1314
|
+
# climate.add_flag('--absolute-path', aliases: [ '-abs', '-p' ])
|
1315
|
+
#
|
1316
|
+
# ==== Specification(s) of a flag (multiple statements)
|
1317
|
+
#
|
1318
|
+
# climate.add_flag('--mark-missing')
|
1319
|
+
# climate.add_alias('--mark-missing', '-x')
|
1320
|
+
#
|
1321
|
+
# climate.add_flag('--absolute-path')
|
1322
|
+
# climate.add_alias('--absolute-path', '-abs', '-p')
|
1323
|
+
#
|
1324
|
+
# ==== Specification(s) of an option (single statement)
|
1325
|
+
#
|
1326
|
+
# climate.add_option('--add-patterns', alias: '-p')
|
1327
|
+
#
|
1328
|
+
# ==== Specification(s) of an option (multiple statements)
|
1329
|
+
#
|
1330
|
+
# climate.add_option('--add-patterns')
|
1331
|
+
# climate.add_alias('--add-patterns', '-p')
|
1332
|
+
#
|
1333
|
+
# ==== Specification of a valued option (which has to be multiple statements)
|
1334
|
+
#
|
1335
|
+
# climate.add_option('--verbosity', values: [ 'succinct', 'verbose' ])
|
1336
|
+
# climate.add_alias('--verbosity=succinct', '-s')
|
1337
|
+
# climate.add_alias('--verbosity=verbose', '-v')
|
1338
|
+
def add_alias(name_or_specification, *aliases)
|
1339
|
+
|
1340
|
+
check_parameter name_or_specification, 'name_or_specification', allow_nil: false, types: [ ::String, ::Symbol, ::CLASP::FlagSpecification, ::CLASP::OptionSpecification ]
|
1341
|
+
raise ArgumentError, "must supply at least one alias" if aliases.empty?
|
1342
|
+
|
1343
|
+
case name_or_specification
|
1344
|
+
when ::CLASP::FlagSpecification
|
1345
|
+
|
1346
|
+
self.specifications << name_or_specification
|
1347
|
+
when ::CLASP::OptionSpecification
|
1348
|
+
|
1349
|
+
self.specifications << name_or_specification
|
1350
|
+
else
|
1351
|
+
|
1352
|
+
self.specifications << CLASP.Alias(name_or_specification, aliases: aliases)
|
1353
|
+
end
|
1354
|
+
end
|
1355
|
+
|
1356
|
+
# Attaches a block to an already-registered flag
|
1357
|
+
#
|
1358
|
+
# === Signature
|
1359
|
+
#
|
1360
|
+
# * *Parameters:*
|
1361
|
+
# - +name_or_flag+ (String, ::CLASP::FlagSpecification) The flag name or instance of CLASP::FlagSpecification
|
1362
|
+
# - +options+ (Hash) An options hash, containing any of the following options. No options are recognised currently
|
1363
|
+
#
|
1364
|
+
# * *Options:*
|
1365
|
+
#
|
1366
|
+
# * *Block* A required block that is invoked when the parsed command-line contains the given flag, receiving the argument and the alias
|
1367
|
+
#
|
1368
|
+
def on_flag(name_or_flag, options={}, &blk)
|
1369
|
+
|
1370
|
+
check_parameter name_or_flag, 'name_or_flag', allow_nil: false, types: [ ::String, ::Symbol, ::CLASP::FlagSpecification ]
|
1371
|
+
|
1372
|
+
raise ArgumentError, "on_flag() requires a block to be given" unless block_given?
|
1373
|
+
|
1374
|
+
specifications.each do |spec|
|
1375
|
+
|
1376
|
+
case spec
|
1377
|
+
when CLASP::FlagSpecification
|
1378
|
+
|
1379
|
+
if spec == name_or_flag
|
1380
|
+
|
1381
|
+
spec.action = blk
|
1382
|
+
|
1383
|
+
return true
|
1384
|
+
end
|
1385
|
+
end
|
1386
|
+
end
|
1387
|
+
|
1388
|
+
warn "The Climate instance does not contain a FlagSpecification matching '#{name_or_flag}' (#{name_or_flag.class})"
|
1389
|
+
|
1390
|
+
false
|
1391
|
+
end
|
1392
|
+
|
1393
|
+
# Attaches a block to an already-registered option
|
1394
|
+
#
|
1395
|
+
# === Signature
|
1396
|
+
#
|
1397
|
+
# * *Parameters:*
|
1398
|
+
# - +name_or_option+ (String, ::CLASP::OptionSpecification) The option name or instance of CLASP::OptionSpecification
|
1399
|
+
# - +options+ (Hash) An options hash, containing any of the following options. No options are recognised currently
|
1400
|
+
#
|
1401
|
+
# * *Options:*
|
1402
|
+
#
|
1403
|
+
# * *Block* A required block that is invoked when the parsed command-line contains the given option, receiving the argument and the alias
|
1404
|
+
#
|
1405
|
+
def on_option(name_or_option, options={}, &blk)
|
1406
|
+
|
1407
|
+
check_parameter name_or_option, 'name_or_option', allow_nil: false, types: [ ::String, ::Symbol, ::CLASP::OptionSpecification ]
|
1408
|
+
|
1409
|
+
raise ArgumentError, "on_option() requires a block to be given" unless block_given?
|
1410
|
+
|
1411
|
+
specifications.each do |spec|
|
1412
|
+
|
1413
|
+
case spec
|
1414
|
+
when CLASP::OptionSpecification
|
1415
|
+
|
1416
|
+
if spec == name_or_option
|
1417
|
+
|
1418
|
+
spec.action = blk
|
1419
|
+
|
1420
|
+
return true
|
1421
|
+
end
|
1422
|
+
end
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
warn "The Climate instance does not contain an OptionSpecification matching '#{name_or_option}' (#{name_or_option.class})"
|
1426
1426
|
|
1427
|
-
|
1428
|
-
|
1427
|
+
false
|
1428
|
+
end
|
1429
1429
|
end # class Climate
|
1430
1430
|
end # module LibCLImate
|
1431
1431
|
|