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.
- 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 +1028 -994
- data/lib/libclimate/libclimate.rb +7 -6
- data/lib/libclimate/version.rb +19 -19
- 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 -233
- data/test/unit/tc_with_blocks.rb +18 -18
- data/test/unit/tc_with_blocks_CLASP.rb +18 -18
- metadata +14 -14
data/lib/libclimate/climate.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
|
2
2
|
# ######################################################################## #
|
3
|
-
# File:
|
3
|
+
# File: lib/libclimate/climate.rb
|
4
4
|
#
|
5
|
-
# Purpose:
|
5
|
+
# Purpose: Definition of the ::LibCLImate::Climate class
|
6
6
|
#
|
7
|
-
# Created:
|
8
|
-
# Updated:
|
7
|
+
# Created: 13th July 2015
|
8
|
+
# Updated: 6th March 2025
|
9
9
|
#
|
10
|
-
# Home:
|
10
|
+
# Home: http://github.com/synesissoftware/libCLImate.Ruby
|
11
11
|
#
|
12
|
-
# Author:
|
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:
|
@@ -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 = '<
|
157
|
-
# cl.constrain_values = 1..
|
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
|
-
|
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
|
-
|
172
|
-
|
201
|
+
# (Array) unchanged copy of the original array of arguments passed to parse
|
202
|
+
attr_reader :argv_original_copy
|
173
203
|
|
174
|
-
|
204
|
+
# (Array) a frozen array of specifications
|
205
|
+
attr_reader :specifications
|
175
206
|
|
176
|
-
|
207
|
+
# (String) The program name
|
208
|
+
attr_reader :program_name
|
177
209
|
|
178
|
-
|
210
|
+
# (String) A (frozen) array of flags
|
211
|
+
attr_reader :flags
|
179
212
|
|
180
|
-
|
181
|
-
|
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
|
-
|
191
|
-
|
216
|
+
# (String) A (frozen) array of values
|
217
|
+
attr_reader :values
|
192
218
|
|
193
|
-
|
194
|
-
attr_reader :argv
|
219
|
+
attr_reader :double_slash_index
|
195
220
|
|
196
|
-
|
197
|
-
|
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
|
-
|
200
|
-
attr_reader :specifications
|
226
|
+
if v = options[:raise]
|
201
227
|
|
202
|
-
|
203
|
-
attr_reader :program_name
|
228
|
+
hm = {}
|
204
229
|
|
205
|
-
|
206
|
-
|
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
|
-
|
209
|
-
|
234
|
+
options = options.merge hm
|
235
|
+
end
|
210
236
|
|
211
|
-
|
212
|
-
|
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
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
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
|
-
|
250
|
+
# 1.a Flags
|
222
251
|
|
223
|
-
|
252
|
+
self.flags.each do |flag|
|
224
253
|
|
225
|
-
|
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
|
-
|
230
|
-
|
256
|
+
sp.kind_of?(::CLASP::FlagSpecification) && flag.name == sp.name
|
257
|
+
end
|
231
258
|
|
232
|
-
|
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
|
-
|
238
|
-
|
239
|
-
|
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
|
-
|
267
|
+
message = make_abort_message_("unrecognised flag '#{f}'")
|
246
268
|
|
247
|
-
|
269
|
+
if false
|
248
270
|
|
249
|
-
|
271
|
+
elsif climate.ignore_unknown
|
250
272
|
|
251
|
-
|
252
|
-
|
273
|
+
;
|
274
|
+
elsif raise_on_unrecognised
|
253
275
|
|
254
|
-
|
276
|
+
if raise_on_unrecognised.is_a?(Class)
|
255
277
|
|
256
|
-
|
278
|
+
raise raise_on_unrecognised, message
|
279
|
+
else
|
257
280
|
|
258
|
-
|
259
|
-
|
260
|
-
|
281
|
+
raise RuntimeError, message
|
282
|
+
end
|
283
|
+
elsif climate.exit_on_unknown
|
261
284
|
|
262
|
-
|
285
|
+
climate.abort message
|
286
|
+
else
|
263
287
|
|
264
|
-
|
288
|
+
if program_name && !program_name.empty?
|
265
289
|
|
266
|
-
|
290
|
+
message = "#{program_name}: #{message}"
|
291
|
+
end
|
267
292
|
|
268
|
-
|
269
|
-
|
293
|
+
climate.stderr.puts message
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
270
297
|
|
271
|
-
|
298
|
+
# 1.b Options
|
272
299
|
|
273
|
-
|
274
|
-
else
|
300
|
+
self.options.each do |option|
|
275
301
|
|
276
|
-
|
277
|
-
end
|
278
|
-
elsif climate.exit_on_unknown
|
302
|
+
spec = specifications.detect do |sp|
|
279
303
|
|
280
|
-
|
281
|
-
|
304
|
+
sp.kind_of?(::CLASP::OptionSpecification) && option.name == sp.name
|
305
|
+
end
|
282
306
|
|
283
|
-
|
307
|
+
if spec
|
284
308
|
|
285
|
-
|
286
|
-
end
|
309
|
+
if spec.respond_to?(:action) && !spec.action.nil?
|
287
310
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
end
|
311
|
+
spec.action.call(option, spec)
|
312
|
+
end
|
313
|
+
else
|
292
314
|
|
293
|
-
|
315
|
+
message = make_abort_message_("unrecognised option '#{f}'")
|
294
316
|
|
295
|
-
|
317
|
+
if false
|
296
318
|
|
297
|
-
|
319
|
+
elsif climate.ignore_unknown
|
298
320
|
|
299
|
-
|
300
|
-
|
321
|
+
;
|
322
|
+
elsif raise_on_unrecognised
|
301
323
|
|
302
|
-
|
324
|
+
if raise_on_unrecognised.is_a?(Class)
|
303
325
|
|
304
|
-
|
326
|
+
raise raise_on_unrecognised, message
|
327
|
+
else
|
305
328
|
|
306
|
-
|
307
|
-
|
308
|
-
|
329
|
+
raise RuntimeError, message
|
330
|
+
end
|
331
|
+
elsif climate.exit_on_unknown
|
309
332
|
|
310
|
-
|
333
|
+
climate.abort message
|
334
|
+
else
|
311
335
|
|
312
|
-
|
336
|
+
if program_name && !program_name.empty?
|
313
337
|
|
314
|
-
|
338
|
+
message = "#{program_name}: #{message}"
|
339
|
+
end
|
315
340
|
|
316
|
-
|
317
|
-
|
341
|
+
climate.stderr.puts message
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
318
345
|
|
319
|
-
|
346
|
+
# 2. police any required options
|
320
347
|
|
321
|
-
|
322
|
-
else
|
348
|
+
climate.check_required_options_(specifications, self.options, [], raise_on_required)
|
323
349
|
|
324
|
-
|
325
|
-
end
|
326
|
-
elsif climate.exit_on_unknown
|
350
|
+
# 3. Check values
|
327
351
|
|
328
|
-
|
329
|
-
|
352
|
+
climate.check_value_constraints_(values)
|
353
|
+
end
|
330
354
|
|
331
|
-
|
355
|
+
def flag_is_specified(id)
|
332
356
|
|
333
|
-
|
334
|
-
|
357
|
+
!@arguments.find_flag(id).nil?
|
358
|
+
end
|
335
359
|
|
336
|
-
|
337
|
-
end
|
338
|
-
end
|
339
|
-
end
|
360
|
+
def lookup_flag(id)
|
340
361
|
|
341
|
-
|
362
|
+
@arguments.find_flag(id)
|
363
|
+
end
|
342
364
|
|
343
|
-
|
365
|
+
def lookup_option(id)
|
344
366
|
|
345
|
-
|
367
|
+
@arguments.find_option(id)
|
368
|
+
end
|
346
369
|
|
347
|
-
|
348
|
-
end
|
370
|
+
end # end class ParseResults
|
349
371
|
|
350
|
-
def flag_is_specified(id)
|
351
372
|
|
352
|
-
|
353
|
-
end
|
373
|
+
#:stopdoc:
|
354
374
|
|
355
|
-
|
375
|
+
private
|
376
|
+
module Climate_Constants_
|
356
377
|
|
357
|
-
|
358
|
-
|
378
|
+
GIVEN_SPECS_ = "_Given_Specs_01B59422_8407_4c89_9432_8160C52BD5AD"
|
379
|
+
end # module Climate_Constants_
|
359
380
|
|
360
|
-
|
381
|
+
def make_abort_message_(msg)
|
361
382
|
|
362
|
-
|
363
|
-
end
|
383
|
+
if 0 != (usage_help_suffix || 0).size
|
364
384
|
|
365
|
-
|
385
|
+
"#{msg}; #{usage_help_suffix}"
|
386
|
+
else
|
366
387
|
|
388
|
+
msg
|
389
|
+
end
|
390
|
+
end
|
367
391
|
|
368
|
-
|
392
|
+
def show_usage_()
|
369
393
|
|
370
|
-
|
371
|
-
|
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
|
-
|
374
|
-
|
400
|
+
CLASP.show_usage specifications, options
|
401
|
+
end
|
375
402
|
|
376
|
-
|
403
|
+
def show_version_()
|
377
404
|
|
378
|
-
|
405
|
+
CLASP.show_version specifications, stream: stdout, program_name: program_name, version: version, exit: exit_on_usage ? 0 : nil
|
406
|
+
end
|
379
407
|
|
380
|
-
|
381
|
-
else
|
408
|
+
def infer_version_(ctxt)
|
382
409
|
|
383
|
-
|
384
|
-
|
385
|
-
|
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
|
-
|
416
|
+
if ctxt
|
388
417
|
|
389
|
-
|
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
|
-
|
396
|
-
end
|
420
|
+
return ctxt.const_get(:PROGRAM_VERSION) if ctxt.const_defined? :PROGRAM_VERSION
|
397
421
|
|
398
|
-
|
422
|
+
ver = []
|
399
423
|
|
400
|
-
|
401
|
-
end
|
424
|
+
if ctxt.const_defined? :PROGRAM_VER_MAJOR
|
402
425
|
|
403
|
-
|
426
|
+
ver << ctxt.const_get(:PROGRAM_VER_MAJOR)
|
404
427
|
|
405
|
-
|
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
|
-
|
430
|
+
ver << ctxt.const_get(:PROGRAM_VER_MINOR)
|
412
431
|
|
413
|
-
|
432
|
+
if ctxt.const_defined?(:PROGRAM_VER_REVISION) || ctxt.const_defined?(:PROGRAM_VER_PATCH)
|
414
433
|
|
415
|
-
|
434
|
+
if ctxt.const_defined?(:PROGRAM_VER_PATCH)
|
416
435
|
|
417
|
-
|
436
|
+
ver << ctxt.const_get(:PROGRAM_VER_PATCH)
|
437
|
+
else
|
418
438
|
|
419
|
-
|
439
|
+
ver << ctxt.const_get(:PROGRAM_VER_REVISION)
|
440
|
+
end
|
420
441
|
|
421
|
-
|
442
|
+
if ctxt.const_defined? :PROGRAM_VER_BUILD
|
422
443
|
|
423
|
-
|
444
|
+
ver << ctxt.const_get(:PROGRAM_VER_BUILD)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
424
448
|
|
425
|
-
|
449
|
+
return ver
|
450
|
+
end
|
451
|
+
else
|
426
452
|
|
427
|
-
|
453
|
+
return PROGRAM_VERSION if defined? PROGRAM_VERSION
|
428
454
|
|
429
|
-
|
455
|
+
ver = []
|
430
456
|
|
431
|
-
|
432
|
-
else
|
457
|
+
if defined? PROGRAM_VER_MAJOR
|
433
458
|
|
434
|
-
|
435
|
-
end
|
459
|
+
ver << PROGRAM_VER_MAJOR
|
436
460
|
|
437
|
-
|
461
|
+
if defined? PROGRAM_VER_MINOR
|
438
462
|
|
439
|
-
|
440
|
-
end
|
441
|
-
end
|
442
|
-
end
|
463
|
+
ver << PROGRAM_VER_MINOR
|
443
464
|
|
444
|
-
|
445
|
-
end
|
446
|
-
else
|
465
|
+
if defined?(PROGRAM_VER_REVISION) || defined?(PROGRAM_VER_PATCH)
|
447
466
|
|
448
|
-
|
467
|
+
if defined?(PROGRAM_VER_PATCH)
|
449
468
|
|
450
|
-
|
469
|
+
ver << PROGRAM_VER_PATCH
|
470
|
+
else
|
451
471
|
|
452
|
-
|
472
|
+
ver << PROGRAM_VER_REVISION
|
473
|
+
end
|
453
474
|
|
454
|
-
|
475
|
+
if defined? PROGRAM_VER_BUILD
|
455
476
|
|
456
|
-
|
477
|
+
ver << PROGRAM_VER_BUILD
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
457
481
|
|
458
|
-
|
482
|
+
return ver
|
483
|
+
end
|
484
|
+
end
|
459
485
|
|
460
|
-
|
486
|
+
nil
|
487
|
+
end
|
461
488
|
|
462
|
-
|
489
|
+
def self.check_type_(v, types)
|
463
490
|
|
464
|
-
|
465
|
-
else
|
491
|
+
return true if v.nil?
|
466
492
|
|
467
|
-
|
468
|
-
end
|
493
|
+
types = [ types ] unless Array === types
|
469
494
|
|
470
|
-
|
495
|
+
return true if types.empty?
|
471
496
|
|
472
|
-
|
473
|
-
end
|
474
|
-
end
|
475
|
-
end
|
497
|
+
types.each do |type|
|
476
498
|
|
477
|
-
|
478
|
-
end
|
479
|
-
end
|
499
|
+
if false
|
480
500
|
|
481
|
-
|
482
|
-
|
501
|
+
;
|
502
|
+
elsif :boolean == type
|
483
503
|
|
484
|
-
|
504
|
+
return true if [ TrueClass, FalseClass ].include? v.class
|
505
|
+
elsif type.is_a?(Class)
|
485
506
|
|
486
|
-
|
507
|
+
return true if v.is_a?(type)
|
508
|
+
elsif type.is_a?(Array)
|
487
509
|
|
488
|
-
|
510
|
+
t0 = type[0]
|
489
511
|
|
490
|
-
|
512
|
+
if t0
|
491
513
|
|
492
|
-
|
514
|
+
#return true if v.is_a?
|
515
|
+
else
|
493
516
|
|
494
|
-
|
517
|
+
# Can be array of anything
|
495
518
|
|
496
|
-
|
497
|
-
|
519
|
+
return true if v.is_a?(Array)
|
520
|
+
end
|
521
|
+
else
|
498
522
|
|
499
|
-
|
500
|
-
|
523
|
+
warn "Cannot validate type of '#{v}' (#{v.class}) against type specification '#{type}'"
|
524
|
+
end
|
525
|
+
end
|
501
526
|
|
502
|
-
|
503
|
-
|
527
|
+
false
|
528
|
+
end
|
504
529
|
|
505
|
-
|
530
|
+
def self.lookup_element_(h, types, name, path)
|
506
531
|
|
507
|
-
|
532
|
+
if h.has_key?(name)
|
508
533
|
|
509
|
-
|
510
|
-
else
|
534
|
+
r = h[name]
|
511
535
|
|
512
|
-
|
536
|
+
unless self.check_type_(r, types)
|
513
537
|
|
514
|
-
|
515
|
-
|
516
|
-
else
|
538
|
+
raise TypeError, "element '#{name}' is of type '#{r.class}' and '#{types}' is required"
|
539
|
+
end
|
517
540
|
|
518
|
-
|
519
|
-
|
520
|
-
end
|
541
|
+
return r
|
542
|
+
end
|
521
543
|
|
522
|
-
|
523
|
-
|
544
|
+
nil
|
545
|
+
end
|
524
546
|
|
525
|
-
|
547
|
+
def self.require_element_(h, types, name, path)
|
526
548
|
|
527
|
-
|
549
|
+
unless h.has_key?(name)
|
528
550
|
|
529
|
-
|
551
|
+
if (path || '').empty?
|
530
552
|
|
531
|
-
|
553
|
+
raise ArgumentError, "missing top-level element '#{name}' in load configuration"
|
554
|
+
else
|
532
555
|
|
533
|
-
|
534
|
-
|
556
|
+
raise ArgumentError, "missing element '#{path}/#{name}' in load configuration"
|
557
|
+
end
|
558
|
+
else
|
535
559
|
|
536
|
-
|
537
|
-
end
|
560
|
+
r = h[name]
|
538
561
|
|
539
|
-
|
540
|
-
end
|
562
|
+
unless self.check_type_(r, types)
|
541
563
|
|
542
|
-
|
564
|
+
raise TypeError, "element '#{name}' is of type '#{r.class}' and '#{types}' is required"
|
565
|
+
end
|
543
566
|
|
544
|
-
|
567
|
+
return r
|
568
|
+
end
|
569
|
+
end
|
545
570
|
|
546
|
-
|
571
|
+
public
|
572
|
+
def check_required_options_(specifications, options, missing, raise_on_required)
|
547
573
|
|
548
|
-
|
549
|
-
else
|
574
|
+
required_specifications = specifications.select do |sp|
|
550
575
|
|
551
|
-
|
552
|
-
|
553
|
-
else
|
576
|
+
sp.kind_of?(::CLASP::OptionSpecification) && sp.required?
|
577
|
+
end
|
554
578
|
|
555
|
-
|
579
|
+
required_specifications = Hash[required_specifications.map { |sp| [ sp.name, sp ] }]
|
556
580
|
|
557
|
-
|
581
|
+
given_options = Hash[options.map { |o| [ o.name, o ]}]
|
558
582
|
|
559
|
-
|
560
|
-
end
|
583
|
+
required_specifications.each do |k, sp|
|
561
584
|
|
562
|
-
|
563
|
-
end
|
564
|
-
end
|
585
|
+
unless given_options.has_key? k
|
565
586
|
|
566
|
-
|
567
|
-
def check_required_options_(specifications, options, missing, raise_on_required)
|
587
|
+
message = sp.required_message
|
568
588
|
|
569
|
-
|
589
|
+
if false
|
570
590
|
|
571
|
-
|
572
|
-
|
591
|
+
;
|
592
|
+
elsif raise_on_required
|
573
593
|
|
574
|
-
|
594
|
+
if raise_on_required.is_a?(Class)
|
575
595
|
|
576
|
-
|
596
|
+
raise raise_on_required, message
|
597
|
+
else
|
577
598
|
|
578
|
-
|
599
|
+
raise RuntimeError, message
|
600
|
+
end
|
601
|
+
elsif exit_on_missing
|
579
602
|
|
580
|
-
|
603
|
+
self.abort message
|
604
|
+
else
|
581
605
|
|
582
|
-
|
606
|
+
if program_name && !program_name.empty?
|
583
607
|
|
584
|
-
|
608
|
+
message = "#{program_name}: #{message}"
|
609
|
+
end
|
585
610
|
|
586
|
-
|
587
|
-
|
611
|
+
stderr.puts message
|
612
|
+
end
|
588
613
|
|
589
|
-
|
614
|
+
missing << sp
|
615
|
+
#results[:missing_option_aliases] << sp
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
590
619
|
|
591
|
-
|
592
|
-
else
|
620
|
+
def check_value_constraints_(values)
|
593
621
|
|
594
|
-
|
595
|
-
end
|
596
|
-
elsif exit_on_missing
|
622
|
+
# now police the values
|
597
623
|
|
598
|
-
|
599
|
-
|
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
|
-
|
628
|
+
case values_constraint
|
629
|
+
when nil
|
602
630
|
|
603
|
-
|
604
|
-
|
631
|
+
;
|
632
|
+
when ::Array
|
605
633
|
|
606
|
-
|
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
|
-
|
610
|
-
#results[:missing_option_aliases] << sp
|
611
|
-
end
|
612
|
-
end
|
613
|
-
end
|
636
|
+
unless values_constraint.include? values.size
|
614
637
|
|
615
|
-
|
638
|
+
message = make_abort_message_("wrong number of values: #{values.size} given, #{values_constraint} required")
|
616
639
|
|
617
|
-
|
640
|
+
if exit_on_missing
|
618
641
|
|
619
|
-
|
620
|
-
|
621
|
-
val_names = ::Array === value_names ? value_names : []
|
642
|
+
self.abort message
|
643
|
+
else
|
622
644
|
|
623
|
-
|
624
|
-
when nil
|
645
|
+
if program_name && !program_name.empty?
|
625
646
|
|
626
|
-
|
627
|
-
|
647
|
+
message = "#{program_name}: #{message}"
|
648
|
+
end
|
628
649
|
|
629
|
-
|
650
|
+
stderr.puts message
|
651
|
+
end
|
652
|
+
end
|
653
|
+
when ::Integer
|
630
654
|
|
631
|
-
|
655
|
+
unless values.size == values_constraint
|
632
656
|
|
633
|
-
|
657
|
+
if name = val_names[values.size]
|
634
658
|
|
635
|
-
|
659
|
+
message = make_abort_message_(name + ' not specified')
|
660
|
+
else
|
636
661
|
|
637
|
-
|
638
|
-
|
662
|
+
message = make_abort_message_("wrong number of values: #{values.size} given, #{values_constraint} required")
|
663
|
+
end
|
639
664
|
|
640
|
-
|
665
|
+
if exit_on_missing
|
641
666
|
|
642
|
-
|
643
|
-
|
667
|
+
self.abort message
|
668
|
+
else
|
644
669
|
|
645
|
-
|
646
|
-
end
|
647
|
-
end
|
648
|
-
when ::Integer
|
670
|
+
if program_name && !program_name.empty?
|
649
671
|
|
650
|
-
|
672
|
+
message = "#{program_name}: #{message}"
|
673
|
+
end
|
651
674
|
|
652
|
-
|
675
|
+
stderr.puts message
|
676
|
+
end
|
677
|
+
end
|
678
|
+
when ::Range
|
653
679
|
|
654
|
-
|
655
|
-
else
|
680
|
+
unless values_constraint.include? values.size
|
656
681
|
|
657
|
-
|
658
|
-
end
|
682
|
+
if name = val_names[values.size]
|
659
683
|
|
660
|
-
|
684
|
+
message = make_abort_message_(name + ' not specified')
|
685
|
+
else
|
661
686
|
|
662
|
-
|
663
|
-
|
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
|
-
|
690
|
+
if exit_on_missing
|
666
691
|
|
667
|
-
|
668
|
-
|
692
|
+
self.abort message
|
693
|
+
else
|
669
694
|
|
670
|
-
|
671
|
-
end
|
672
|
-
end
|
673
|
-
when ::Range
|
695
|
+
if program_name && !program_name.empty?
|
674
696
|
|
675
|
-
|
697
|
+
message = "#{program_name}: #{message}"
|
698
|
+
end
|
676
699
|
|
677
|
-
|
700
|
+
stderr.puts message
|
701
|
+
end
|
702
|
+
end
|
703
|
+
else
|
678
704
|
|
679
|
-
|
680
|
-
|
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
|
-
|
683
|
-
end
|
710
|
+
public
|
684
711
|
|
685
|
-
|
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
|
-
|
688
|
-
else
|
730
|
+
check_parameter options, 'options', allow_nil: true, type: ::Hash
|
689
731
|
|
690
|
-
|
732
|
+
options ||= {}
|
691
733
|
|
692
|
-
|
693
|
-
end
|
734
|
+
h = nil
|
694
735
|
|
695
|
-
|
696
|
-
|
697
|
-
end
|
698
|
-
else
|
736
|
+
case source
|
737
|
+
when ::IO
|
699
738
|
|
700
|
-
|
701
|
-
|
702
|
-
end
|
703
|
-
#:startdoc:
|
739
|
+
h = YAML.load source.read
|
740
|
+
when ::Hash
|
704
741
|
|
705
|
-
|
742
|
+
h = source
|
743
|
+
else
|
706
744
|
|
707
|
-
|
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
|
-
|
747
|
+
h = source.to_hash
|
748
|
+
else
|
726
749
|
|
727
|
-
|
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
|
-
|
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
|
-
|
732
|
-
when ::IO
|
767
|
+
specs = CLASP::Arguments.load_specifications _libclimate, options
|
733
768
|
|
734
|
-
|
735
|
-
when ::Hash
|
769
|
+
cl = Climate.new(options.merge(Climate_Constants_::GIVEN_SPECS_ => specs), &blk)
|
736
770
|
|
737
|
-
|
738
|
-
|
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
|
-
|
783
|
+
cl
|
784
|
+
end
|
741
785
|
|
742
|
-
|
743
|
-
|
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+
|
744
988
|
|
745
|
-
|
746
|
-
end
|
747
|
-
end
|
989
|
+
raise ArgumentError, "argv may not be nil" if argv.nil?
|
748
990
|
|
749
|
-
|
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
|
-
|
993
|
+
run_ argv, arguments, **options
|
994
|
+
end
|
763
995
|
|
764
|
-
|
996
|
+
private
|
997
|
+
def run_(argv, arguments, **opts) # :nodoc:
|
765
998
|
|
766
|
-
|
767
|
-
|
768
|
-
|
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
|
-
|
779
|
-
end
|
1003
|
+
double_slash_index = arguments.double_slash_index
|
780
1004
|
|
781
|
-
|
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> | <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
|
-
|
1007
|
+
flags: {
|
982
1008
|
|
983
|
-
|
1009
|
+
given: flags,
|
1010
|
+
handled: [],
|
1011
|
+
unhandled: [],
|
1012
|
+
unknown: [],
|
1013
|
+
},
|
984
1014
|
|
985
|
-
|
986
|
-
end
|
1015
|
+
options: {
|
987
1016
|
|
988
|
-
|
989
|
-
|
1017
|
+
given: options,
|
1018
|
+
handled: [],
|
1019
|
+
unhandled: [],
|
1020
|
+
unknown: [],
|
1021
|
+
},
|
990
1022
|
|
991
|
-
|
992
|
-
options = arguments.options
|
993
|
-
values = arguments.values.to_a
|
1023
|
+
values: values,
|
994
1024
|
|
995
|
-
|
1025
|
+
missing_option_aliases: [],
|
1026
|
+
}
|
996
1027
|
|
997
|
-
|
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
|
-
|
1036
|
+
# 1.a Flags
|
1000
1037
|
|
1001
|
-
|
1002
|
-
handled: [],
|
1003
|
-
unhandled: [],
|
1004
|
-
unknown: [],
|
1005
|
-
},
|
1038
|
+
flags.each do |f|
|
1006
1039
|
|
1007
|
-
|
1040
|
+
spec = specifications.detect do |sp|
|
1008
1041
|
|
1009
|
-
|
1010
|
-
|
1011
|
-
unhandled: [],
|
1012
|
-
unknown: [],
|
1013
|
-
},
|
1042
|
+
sp.kind_of?(::CLASP::FlagSpecification) && f.name == sp.name
|
1043
|
+
end
|
1014
1044
|
|
1015
|
-
|
1045
|
+
if spec
|
1016
1046
|
|
1017
|
-
|
1018
|
-
}
|
1047
|
+
selector = :unhandled
|
1019
1048
|
|
1020
|
-
|
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
|
-
|
1051
|
+
spec.action.call(f, spec)
|
1029
1052
|
|
1030
|
-
|
1053
|
+
selector = :handled
|
1054
|
+
end
|
1031
1055
|
|
1032
|
-
|
1056
|
+
results[:flags][selector] << f
|
1057
|
+
else
|
1033
1058
|
|
1034
|
-
|
1035
|
-
end
|
1059
|
+
message = make_abort_message_("unrecognised flag '#{f}'")
|
1036
1060
|
|
1037
|
-
|
1061
|
+
if false
|
1038
1062
|
|
1039
|
-
|
1063
|
+
elsif ignore_unknown
|
1040
1064
|
|
1041
|
-
|
1065
|
+
;
|
1066
|
+
elsif exit_on_unknown
|
1042
1067
|
|
1043
|
-
|
1068
|
+
self.abort message
|
1069
|
+
else
|
1044
1070
|
|
1045
|
-
|
1046
|
-
end
|
1071
|
+
if program_name && !program_name.empty?
|
1047
1072
|
|
1048
|
-
|
1049
|
-
|
1073
|
+
message = "#{program_name}: #{message}"
|
1074
|
+
end
|
1050
1075
|
|
1051
|
-
|
1076
|
+
stderr.puts message
|
1077
|
+
end
|
1052
1078
|
|
1053
|
-
|
1079
|
+
results[:flags][:unknown] << f
|
1080
|
+
end
|
1081
|
+
end
|
1054
1082
|
|
1055
|
-
|
1083
|
+
# 1.b Options
|
1056
1084
|
|
1057
|
-
|
1058
|
-
elsif exit_on_unknown
|
1085
|
+
options.each do |o|
|
1059
1086
|
|
1060
|
-
|
1061
|
-
else
|
1087
|
+
spec = specifications.detect do |sp|
|
1062
1088
|
|
1063
|
-
|
1089
|
+
sp.kind_of?(::CLASP::OptionSpecification) && o.name == sp.name
|
1090
|
+
end
|
1064
1091
|
|
1065
|
-
|
1066
|
-
end
|
1092
|
+
if spec
|
1067
1093
|
|
1068
|
-
|
1069
|
-
end
|
1094
|
+
selector = :unhandled
|
1070
1095
|
|
1071
|
-
|
1072
|
-
end
|
1073
|
-
end
|
1096
|
+
if spec.respond_to?(:action) && !spec.action.nil?
|
1074
1097
|
|
1075
|
-
|
1098
|
+
spec.action.call(o, spec)
|
1076
1099
|
|
1077
|
-
|
1100
|
+
selector = :handled
|
1101
|
+
end
|
1078
1102
|
|
1079
|
-
|
1103
|
+
results[:options][selector] << o
|
1104
|
+
else
|
1080
1105
|
|
1081
|
-
|
1082
|
-
end
|
1106
|
+
message = make_abort_message_("unrecognised option '#{o}'")
|
1083
1107
|
|
1084
|
-
|
1108
|
+
if false
|
1085
1109
|
|
1086
|
-
|
1110
|
+
elsif ignore_unknown
|
1087
1111
|
|
1088
|
-
|
1112
|
+
;
|
1113
|
+
elsif exit_on_unknown
|
1089
1114
|
|
1090
|
-
|
1115
|
+
self.abort message
|
1116
|
+
else
|
1091
1117
|
|
1092
|
-
|
1093
|
-
end
|
1118
|
+
if program_name && !program_name.empty?
|
1094
1119
|
|
1095
|
-
|
1096
|
-
|
1120
|
+
message = "#{program_name}: #{message}"
|
1121
|
+
end
|
1097
1122
|
|
1098
|
-
|
1123
|
+
stderr.puts message
|
1124
|
+
end
|
1099
1125
|
|
1100
|
-
|
1126
|
+
results[:options][:unknown] << o
|
1127
|
+
end
|
1128
|
+
end
|
1101
1129
|
|
1102
|
-
|
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
|
-
|
1108
|
-
else
|
1134
|
+
# 3. Check values
|
1109
1135
|
|
1110
|
-
|
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
|
-
|
1119
|
-
end
|
1120
|
-
end
|
1140
|
+
def results.flags
|
1121
1141
|
|
1122
|
-
|
1142
|
+
self[:flags]
|
1143
|
+
end
|
1123
1144
|
|
1124
|
-
|
1145
|
+
def results.options
|
1125
1146
|
|
1126
|
-
|
1147
|
+
self[:options]
|
1148
|
+
end
|
1127
1149
|
|
1128
|
-
|
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
|
-
|
1157
|
+
double_slash_index
|
1158
|
+
end
|
1133
1159
|
|
1134
|
-
|
1135
|
-
end
|
1160
|
+
results.define_singleton_method(:argv) do
|
1136
1161
|
|
1137
|
-
|
1162
|
+
argv
|
1163
|
+
end
|
1138
1164
|
|
1139
|
-
self[:options]
|
1140
|
-
end
|
1141
1165
|
|
1142
|
-
|
1166
|
+
if opts[:value_attributes] || @value_attributes
|
1143
1167
|
|
1144
|
-
|
1145
|
-
end
|
1168
|
+
(value_names || []).each_with_index do |name, index|
|
1146
1169
|
|
1147
|
-
|
1170
|
+
name = name.gsub(/-/, '_').to_sym
|
1148
1171
|
|
1149
|
-
|
1150
|
-
end
|
1172
|
+
if results.respond_to? name
|
1151
1173
|
|
1152
|
-
|
1174
|
+
warn "cannot create attribute `#{name}` on instance of `#{results.class}` because that name is already used"
|
1175
|
+
else
|
1153
1176
|
|
1154
|
-
|
1155
|
-
end
|
1177
|
+
value = results.values[index]
|
1156
1178
|
|
1157
|
-
|
1158
|
-
end
|
1159
|
-
public
|
1179
|
+
results.define_singleton_method(name) do
|
1160
1180
|
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
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
|
-
|
1183
|
-
stream ||= stderr
|
1184
|
-
stream ||= $stderr
|
1188
|
+
results.freeze
|
1185
1189
|
|
1186
|
-
|
1190
|
+
results
|
1191
|
+
end
|
1192
|
+
public
|
1187
1193
|
|
1188
|
-
|
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
|
-
|
1191
|
-
|
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
|
-
|
1395
|
-
|
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
|
|