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