clasp-ruby 0.20.3 → 0.22.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2cdf702af24b59501e6bbe93ba1985416e7793a7dd3dd2a3cfedae7847156a2a
4
+ data.tar.gz: 6efa37c0a5f2914498de32916f6d6be2fbe79ab87d0af3a32f10dc5163595309
5
+ SHA512:
6
+ metadata.gz: 5660b7d456acadbbaeacbfb04f83cfb2dfe94b5918cb731a7194fe0da0efba198e1be3196870fb1ea338cf489e528b703d406e8cee949065e234764181ac2a47
7
+ data.tar.gz: 83c5cbbb81fd29695e7c00f600bcfc5812854534c5dae6d61812e9ee1d9c68e058aa21c493adced37c3121e54cf724f048b7cf7bd700cff8f91ffbb1440499de
@@ -46,7 +46,8 @@
46
46
 
47
47
 
48
48
 
49
- require File.join(File.dirname(__FILE__), 'specifications.rb')
49
+ require File.join(File.dirname(__FILE__), 'specifications')
50
+ require File.join(File.dirname(__FILE__), 'util', 'value_parser')
50
51
 
51
52
  require 'yaml'
52
53
 
@@ -136,17 +137,40 @@ class Arguments
136
137
  # Class that represents a parsed option
137
138
  class OptionArgument
138
139
 
140
+ include ::CLASP::Util::ValueParser
141
+
139
142
  # @!visibility private
140
143
  #
141
144
  # [PRIVATE] This method is subject to changed between versions and
142
145
  # should not be called directly from application code
143
146
  def initialize(arg, given_index, given_name, resolved_name, argument_spec, given_hyphens, given_label, value, extras) # :nodoc:
144
147
 
145
- actual_value = value
148
+ resolved_value = nil
149
+
150
+ if argument_spec
151
+
152
+ case constraint = (argument_spec.constraint || {})
153
+ =begin
154
+ when Proc
155
+
156
+ resolved_value = value_from_Proc(constraint, value, arg, given_index, given_name, argument_spec, extras)
157
+ =end
158
+ when Hash
159
+
160
+ if constraint.empty?
161
+
162
+ resolved_value = (value || '').empty? ? argument_spec.default_value : value
163
+ else
164
+
165
+ resolved_value = value_from_Hash(constraint, value, arg, given_index, given_name, argument_spec, extras)
166
+ end
167
+ else
146
168
 
147
- if (value || '').empty? && argument_spec
169
+ warn "unexpected constraint on argument specification #{argument_spec} when parsing argument '#{arg}'"
170
+ end
171
+ else
148
172
 
149
- actual_value = argument_spec.default_value
173
+ resolved_value = value
150
174
  end
151
175
 
152
176
  @arg = arg
@@ -155,7 +179,8 @@ class Arguments
155
179
  @argument_specification = argument_spec
156
180
  @given_hyphens = given_hyphens
157
181
  @given_label = given_label
158
- @value = actual_value
182
+ @given_value = value
183
+ @value = resolved_value
159
184
  @name = resolved_name || given_name
160
185
  @extras = extras.nil? ? {} : extras
161
186
  end
@@ -172,7 +197,9 @@ class Arguments
172
197
  attr_reader :given_label
173
198
  # (String) The resolved name of the argument
174
199
  attr_reader :name
175
- # (String) The value of the option
200
+ # (String) The given value of the option
201
+ attr_reader :given_value
202
+ # (????) The value of the option, which may be of a type other than string subject to the option specification's constraint
176
203
  attr_reader :value
177
204
  # (Object, Hash) The extras associated with the argument
178
205
  attr_reader :extras
@@ -458,7 +485,7 @@ class Arguments
458
485
  values = []
459
486
 
460
487
  forced_value = false
461
- want_option_value = false
488
+ pending_option = nil
462
489
 
463
490
  argv.each_with_index do |arg, index|
464
491
 
@@ -563,15 +590,22 @@ class Arguments
563
590
 
564
591
  if argument_spec and argument_spec.is_a? CLASP::OptionSpecification and not value
565
592
 
566
- want_option_value = true
567
- options << OptionArgument.new(arg, index, given_name, resolved_name, argument_spec, hyphens.size, given_label, nil, argument_spec ? argument_spec.extras : nil)
593
+ pending_option = {
594
+
595
+ arg: arg,
596
+ index: index,
597
+ given_name: given_name,
598
+ resolved_name: resolved_name,
599
+ argument_spec: argument_spec,
600
+ hyphens_size: hyphens.size,
601
+ given_label: given_label,
602
+ extras: argument_spec ? argument_spec.extras : nil,
603
+ }
568
604
  elsif value
569
605
 
570
- want_option_value = false
571
606
  options << OptionArgument.new(arg, index, given_name, resolved_name, argument_spec, hyphens.size, given_label, value, argument_spec ? argument_spec.extras : nil)
572
607
  else
573
608
 
574
- want_option_value = false
575
609
  flags << FlagArgument.new(arg, index, given_name, resolved_name, argument_spec, hyphens.size, given_label, argument_spec ? argument_spec.extras : nil)
576
610
  end
577
611
 
@@ -579,20 +613,31 @@ class Arguments
579
613
  end
580
614
  end
581
615
 
582
- if want_option_value and not forced_value
616
+ if pending_option
583
617
 
584
- option = options[-1]
585
- option.instance_eval("@value='#{arg}'")
586
- want_option_value = false
587
- else
618
+ value = forced_value ? nil : arg
588
619
 
589
- arg = arg.dup
590
- arg_ix = ::Integer === index ? index : index.dup
620
+ options << OptionArgument.new(pending_option[:arg], pending_option[:index], pending_option[:given_name], pending_option[:resolved_name], pending_option[:argument_spec], pending_option[:hyphens_size], pending_option[:given_label], value, pending_option[:extras])
591
621
 
592
- arg.define_singleton_method(:given_index) { arg_ix }
622
+ pending_option = nil
593
623
 
594
- values << arg
624
+ next unless forced_value
595
625
  end
626
+
627
+ arg = arg.dup
628
+ arg_ix = ::Integer === index ? index : index.dup
629
+
630
+ arg.define_singleton_method(:given_index) { arg_ix }
631
+
632
+ values << arg
633
+ end
634
+
635
+ if pending_option
636
+
637
+ value = nil
638
+
639
+ options << OptionArgument.new(pending_option[:arg], pending_option[:index], pending_option[:given_name], pending_option[:resolved_name], pending_option[:argument_spec], pending_option[:hyphens_size], pending_option[:given_label], value, pending_option[:extras])
640
+
596
641
  end
597
642
 
598
643
  return flags, options, values
data/lib/clasp/clasp.rb CHANGED
@@ -50,6 +50,15 @@ require 'clasp/specifications'
50
50
  require 'clasp/cli'
51
51
  require 'clasp/version'
52
52
 
53
+ module CLASP
54
+
55
+ # TBC (but is a shorthand for calling +Arguments.new()+
56
+ def self.parse(argv = ARGV, specifications = nil, options = {})
57
+
58
+ return Arguments.new(argv, specifications, options)
59
+ end
60
+ end # module CLASP
61
+
53
62
  # ############################## end of file ############################# #
54
63
 
55
64
 
@@ -5,7 +5,7 @@
5
5
  # Purpose: Argument specification classes
6
6
  #
7
7
  # Created: 25th October 2014
8
- # Updated: 20th April 2019
8
+ # Updated: 29th April 2019
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/CLASP.Ruby
11
11
  #
@@ -57,8 +57,40 @@ module CLASP
57
57
  # ######################################################################## #
58
58
  # classes
59
59
 
60
+ # @!visibility private
61
+ class SpecificationBase # :nodoc: all
62
+
63
+ private
64
+ # @!visibility private
65
+ def check_arity_(blk, range, label) # :nodoc:
66
+
67
+ raise ArgumentError, "block must be a #{Proc}; #{blk.class} given" unless blk.nil? || Proc === blk
68
+
69
+ if blk
70
+
71
+ case blk.arity
72
+ when range
73
+
74
+ ;
75
+ else
76
+
77
+ msg = "wrong arity for #{label}"
78
+
79
+ if $DEBUG
80
+
81
+ raise ArgumentError, msg
82
+ else
83
+
84
+ warn msg
85
+ end
86
+ end
87
+ end
88
+ end
89
+ public
90
+ end
91
+
60
92
  # A class that represents the specification for a command-line flag
61
- class FlagSpecification
93
+ class FlagSpecification < SpecificationBase
62
94
 
63
95
  # Creates a FlagSpecification instance from the given name, aliases, and help
64
96
  #
@@ -69,12 +101,19 @@ class FlagSpecification
69
101
  # - +aliases+ (+Array+) 0 or more strings specifying short-form or option-value aliases
70
102
  # - +help+ (+String+) The help string, which may be +nil+
71
103
  # - +extras+ An application-defined additional parameter. If +nil+, it is assigned an empty +Hash+
72
- def initialize(name, aliases, help, extras = nil)
104
+ #
105
+ # * *Block* An optional block that is called when a matching flag argument is found
106
+ #
107
+ # *NOTE:* Users should prefer the +CLASP::Flag()+ method
108
+ def initialize(name, aliases, help, extras = nil, &blk)
109
+
110
+ check_arity_(blk, 0..3, "flag")
73
111
 
74
112
  @name = name
75
113
  @aliases = (aliases || []).select { |a| a and not a.empty? }
76
114
  @help = help
77
115
  @extras = extras || {}
116
+ @action = blk
78
117
  end
79
118
 
80
119
  # The flag's name string
@@ -86,6 +125,17 @@ class FlagSpecification
86
125
  # The flag's extras
87
126
  attr_reader :extras
88
127
 
128
+ # (Proc) The procedure
129
+ attr_reader :action
130
+
131
+ # @!visibility private
132
+ def action=(blk) # :nodoc: all
133
+
134
+ check_arity_(blk, 0..3, "flag")
135
+
136
+ @action = blk
137
+ end
138
+
89
139
  # String form of the flag
90
140
  def to_s
91
141
 
@@ -133,28 +183,38 @@ class FlagSpecification
133
183
  @@Version_ = self.new('--version', [], 'shows version and terminates')
134
184
  public
135
185
  # An instance of FlagSpecification that provides default '--help' information
136
- def self.Help(extras = nil)
186
+ #
187
+ # If you wish to specify +extras+ or attach a block, you may do so
188
+ def self.Help(extras = nil, &blk)
137
189
 
138
190
  h = @@Help_
139
191
 
140
- return self.new(h.name, h.aliases, h.help, extras) if extras
192
+ if extras || blk
193
+
194
+ return self.new(h.name, h.aliases, h.help, extras, &blk)
195
+ end
141
196
 
142
197
  h
143
198
  end
144
199
 
145
200
  # An instance of FlagSpecification that provides default '--version' information
146
- def self.Version(extras = nil)
201
+ #
202
+ # If you wish to specify +extras+ or attach a block, you may do so
203
+ def self.Version(extras = nil, &blk)
147
204
 
148
205
  h = @@Version_
149
206
 
150
- return self.new(h.name, h.aliases, h.help, extras) if extras
207
+ if extras || blk
208
+
209
+ return self.new(h.name, h.aliases, h.help, extras, &blk)
210
+ end
151
211
 
152
212
  h
153
213
  end
154
214
  end
155
215
 
156
216
  # A class that represents the specification for a command-line option
157
- class OptionSpecification
217
+ class OptionSpecification < SpecificationBase
158
218
 
159
219
  # Creates an OptionSpecification instance from the given name, aliases, help,
160
220
  # values_range, and default_value
@@ -169,8 +229,15 @@ class OptionSpecification
169
229
  # - +default_value+ (+String+) The default value of the option, which will be used in the case where an option is specified without a value. May be +nil+
170
230
  # - +required+ (boolean) Whether the option is required. May be +nil+
171
231
  # - +required_message+ (::String) Message to be used when reporting that a required option is missing. May be +nil+ in which case a message of the form "<option-name> not specified; use --help for usage". If begins with the nul character ("\0"), then is used in the place of the <option-name> and placed into the rest of the standard form message
232
+ # - +constraint+ (Hash) Constraint to be applied to the parsed values of options matching this specification. NOTE: only integer constraints are supported in the current version
172
233
  # - +extras+ An application-defined additional parameter. If +nil+, it is assigned an empty +Hash+
173
- def initialize(name, aliases, help, values_range, default_value, required, required_message, extras = nil)
234
+ #
235
+ # * *Block* An optional block that is called when a matching option argument is found
236
+ #
237
+ # *NOTE:* Users should prefer the +CLASP::Option()+ method
238
+ def initialize(name, aliases, help, values_range, default_value, required, required_message, constraint, extras = nil, &blk)
239
+
240
+ check_arity_(blk, 0..3, "option")
174
241
 
175
242
  @name = name
176
243
  @aliases = (aliases || []).select { |a| a and not a.empty? }
@@ -179,7 +246,9 @@ class OptionSpecification
179
246
  @default_value = default_value
180
247
  @required = required
181
248
  @required_message = nil
249
+ @constraint = constraint || {}
182
250
  @extras = extras || {}
251
+ @action = blk
183
252
 
184
253
  rm_name = nil
185
254
 
@@ -214,16 +283,28 @@ class OptionSpecification
214
283
  attr_reader :default_value
215
284
  # Indicates whether the option is required
216
285
  def required?; @required; end
217
- # The message to be used when reporting that a required option is
218
- # missing
286
+ # The message to be used when reporting that a required option is missing
219
287
  attr_reader :required_message
288
+ # The value constraint
289
+ attr_reader :constraint
220
290
  # The option's extras
221
291
  attr_reader :extras
222
292
 
293
+ # (Proc) The procedure
294
+ attr_reader :action
295
+
296
+ # @!visibility private
297
+ def action=(blk) # :nodoc: all
298
+
299
+ check_arity_(blk, 0..3, "flag")
300
+
301
+ @action = blk
302
+ end
303
+
223
304
  # String form of the option
224
305
  def to_s
225
306
 
226
- "{#{name}; aliases=#{aliases.join(', ')}; values_range=[ #{values_range.join(', ')} ]; default_value='#{default_value}'; help='#{help}'; required?=#{required?}; extras=#{extras}}"
307
+ "{#{name}; aliases=#{aliases.join(', ')}; values_range=[ #{values_range.join(', ')} ]; default_value='#{default_value}'; help='#{help}'; required?=#{required?}; required_message=#{required_message}; constraint=#{constraint}; extras=#{extras}}"
227
308
  end
228
309
 
229
310
  # @!visibility private
@@ -311,7 +392,9 @@ end
311
392
  # - +:aliases+ (::Array) An array of aliases, e.g. [ '-v', '-verb' ]. Ignored if +:alias+ is specified
312
393
  # - +:extras+ An application-defined object, usually a hash of custom attributes
313
394
  # - +:help+ (::String) A help string
314
- def CLASP.Flag(name, options = {})
395
+ #
396
+ # * *Block* An optional block that is called when a matching flag argument is found
397
+ def CLASP.Flag(name, options = {}, &blk)
315
398
 
316
399
  aliases = nil
317
400
  help = nil
@@ -344,7 +427,7 @@ def CLASP.Flag(name, options = {})
344
427
  end
345
428
  end
346
429
 
347
- CLASP::FlagSpecification.new(name, aliases, help, extras)
430
+ CLASP::FlagSpecification.new(name, aliases, help, extras, &blk)
348
431
  end
349
432
 
350
433
  # Generator method that obtains a CLASP::OptionSpecification according to the given
@@ -366,9 +449,12 @@ end
366
449
  # - +required+ (boolean) Whether the option is required. May be +nil+
367
450
  # - +required_message+ (::String) Message to be used when reporting that a required option is missing. May be +nil+ in which case a message of the form "<option-name> not specified; use --help for usage". If begins with the nul character ("\0"), then is used in the place of the <option-name> and placed into the rest of the standard form message
368
451
  # - +extras+ An application-defined additional parameter. If +nil+, it is assigned an empty +Hash+.
452
+ # - +constraint+ (Hash) Constraint to be applied to the parsed values of options matching this specification. NOTE: only integer constraints are supported in the current version
369
453
  # - +:values_range+ (::Array) An array defining the accepted values for the option
370
454
  # - +:values+ [DEPRECATED] Alternative to +:values_range+
371
- def CLASP.Option(name, options = {})
455
+ #
456
+ # * *Block* An optional block that is called when a matching option argument is found
457
+ def CLASP.Option(name, options = {}, &blk)
372
458
 
373
459
  aliases = nil
374
460
  help = nil
@@ -376,6 +462,7 @@ def CLASP.Option(name, options = {})
376
462
  default_value = nil
377
463
  required = false
378
464
  require_message = nil
465
+ constraint = nil
379
466
  extras = nil
380
467
 
381
468
  options.each do |k, v|
@@ -407,6 +494,9 @@ def CLASP.Option(name, options = {})
407
494
  when :extras
408
495
 
409
496
  extras = v
497
+ when :constraint
498
+
499
+ constraint = v
410
500
  else
411
501
 
412
502
  raise ArgumentError, "invalid option for option: '#{k}' => '#{v}'"
@@ -417,7 +507,7 @@ def CLASP.Option(name, options = {})
417
507
  end
418
508
  end
419
509
 
420
- CLASP::OptionSpecification.new(name, aliases, help, values_range, default_value, required, require_message, extras)
510
+ CLASP::OptionSpecification.new(name, aliases, help, values_range, default_value, required, require_message, constraint, extras, &blk)
421
511
  end
422
512
 
423
513
  def CLASP.Alias(name, *args)
@@ -0,0 +1,82 @@
1
+
2
+ # ######################################################################## #
3
+ # File: clasp/util/exceptions.rb
4
+ #
5
+ # Purpose: Exception classes
6
+ #
7
+ # Created: 20th April 2019
8
+ # Updated: 28th April 2019
9
+ #
10
+ # Home: http://github.com/synesissoftware/CLASP.Ruby
11
+ #
12
+ # Author: Matthew Wilson
13
+ #
14
+ # Copyright (c) 2019, Matthew Wilson and Synesis Software
15
+ # All rights reserved.
16
+ #
17
+ # Redistribution and use in source and binary forms, with or without
18
+ # modification, are permitted provided that the following conditions are
19
+ # met:
20
+ #
21
+ # * Redistributions of source code must retain the above copyright
22
+ # notice, this list of conditions and the following disclaimer.
23
+ #
24
+ # * Redistributions in binary form must reproduce the above copyright
25
+ # notice, this list of conditions and the following disclaimer in the
26
+ # documentation and/or other materials provided with the distribution.
27
+ #
28
+ # * Neither the names of the copyright holder nor the names of its
29
+ # contributors may be used to endorse or promote products derived from
30
+ # this software without specific prior written permission.
31
+ #
32
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
33
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
34
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
36
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
39
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
40
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
+ #
44
+ # ######################################################################## #
45
+
46
+
47
+
48
+ =begin
49
+ =end
50
+
51
+ module CLASP # :nodoc:
52
+
53
+ # Exceptions
54
+ module Exceptions
55
+
56
+ # Root exception for CLASP
57
+ class CLASPException < RuntimeError; end
58
+
59
+ # Root exception for value parsing
60
+ class ValueParserException < CLASPException; end
61
+
62
+ # No value specified (and no default value) for an option
63
+ class MissingValueException < ValueParserException; end
64
+
65
+ # Exception class indicating invalid values (as opposed to types)
66
+ class InvalidValueException < ValueParserException; end
67
+
68
+ # The given value could not be recognised as a (properly-formatted) number
69
+ class InvalidNumberException < InvalidValueException; end
70
+
71
+ # The given value could not be recognised as a (properly-formatted) integer
72
+ class InvalidIntegerException < InvalidNumberException; end
73
+
74
+ # The value was a valid integer but is out of range
75
+ class IntegerOutOfRangeException < InvalidValueException; end
76
+
77
+ end # module Exceptions
78
+ end # module CLASP
79
+
80
+ # ############################## end of file ############################# #
81
+
82
+
@@ -0,0 +1,222 @@
1
+
2
+ # ######################################################################## #
3
+ # File: clasp/util/value_parser.rb
4
+ #
5
+ # Purpose: Utility component for typed values
6
+ #
7
+ # Created: 20th April 2019
8
+ # Updated: 28th April 2019
9
+ #
10
+ # Home: http://github.com/synesissoftware/CLASP.Ruby
11
+ #
12
+ # Author: Matthew Wilson
13
+ #
14
+ # Copyright (c) 2019, Matthew Wilson and Synesis Software
15
+ # All rights reserved.
16
+ #
17
+ # Redistribution and use in source and binary forms, with or without
18
+ # modification, are permitted provided that the following conditions are
19
+ # met:
20
+ #
21
+ # * Redistributions of source code must retain the above copyright
22
+ # notice, this list of conditions and the following disclaimer.
23
+ #
24
+ # * Redistributions in binary form must reproduce the above copyright
25
+ # notice, this list of conditions and the following disclaimer in the
26
+ # documentation and/or other materials provided with the distribution.
27
+ #
28
+ # * Neither the names of the copyright holder nor the names of its
29
+ # contributors may be used to endorse or promote products derived from
30
+ # this software without specific prior written permission.
31
+ #
32
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
33
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
34
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
36
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
39
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
40
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
+ #
44
+ # ######################################################################## #
45
+
46
+
47
+
48
+ require File.join(File.dirname(__FILE__), 'exceptions')
49
+
50
+ =begin
51
+ =end
52
+
53
+ module CLASP # :nodoc:
54
+ module Util # :nodoc:
55
+
56
+ # @!visibility private
57
+ module ValueParser # :nodoc: all
58
+
59
+ module Internal_ # :nodoc: all
60
+
61
+ include Exceptions
62
+
63
+ def self.is_integer? type
64
+
65
+ h = {}
66
+
67
+ return true if Integer == type
68
+ return true if :integer == type
69
+
70
+ false
71
+ end
72
+
73
+ def self.obtain_integer value, constraint, argument_spec
74
+
75
+ # If no value is given, then use the default (and don't do any
76
+ # range testing)
77
+
78
+ if (value || '').empty?
79
+
80
+ def_value = argument_spec.default_value
81
+
82
+ if (def_value || '').to_s.empty?
83
+
84
+ msg = "no value specified for the option '#{argument_spec.name}', which has no default value either"
85
+
86
+ warn msg if $DEBUG
87
+
88
+ raise MissingValueException, msg
89
+ end
90
+
91
+ begin
92
+
93
+ return Integer(def_value)
94
+ rescue ArgumentError => x
95
+
96
+ msg = "default value '#{def_value}' specified for option '#{argument_spec.name}' that requires the value to be an integer"
97
+
98
+ warn msg if $DEBUG
99
+
100
+ raise InvalidIntegerException, msg
101
+ end
102
+ end
103
+
104
+ # obtain the integer from the value
105
+
106
+ v = nil
107
+
108
+ begin
109
+
110
+ v = Integer(value)
111
+ rescue ArgumentError => x
112
+
113
+ msg = "value '#{value}' specified for option '#{argument_spec.name}' that requires the value to be an integer"
114
+
115
+ warn msg if $DEBUG
116
+
117
+ raise InvalidIntegerException, msg
118
+ end
119
+
120
+ # Is there a value constraint?:
121
+ #
122
+ # - values (obtained from argument_spec#values)
123
+ # - range
124
+ # - minimum & maximum
125
+
126
+ values_range = argument_spec.values_range
127
+
128
+ unless values_range.empty?
129
+
130
+ v_s = v.to_s
131
+
132
+ v_s = '+' + v_s unless '-' == v_s[0]
133
+
134
+ vr_s = values_range.map { |x| x.to_s }.map { |x| '-' == x[0] ? x : '+' + x }
135
+
136
+ unless vr_s.include? v_s
137
+
138
+ msg = "given value '#{value}' specified for option '#{argument_spec.name}' does not fall within the required range"
139
+
140
+ raise IntegerOutOfRangeException, msg
141
+ end
142
+ else
143
+
144
+ case range = constraint[:range]
145
+ when :negative
146
+
147
+ if v >= 0
148
+
149
+ msg = "given value '#{value}' specified for option '#{argument_spec.name}' must be a negative integer"
150
+
151
+ raise IntegerOutOfRangeException, msg
152
+ end
153
+ when :positive
154
+
155
+ if v < 1
156
+
157
+ msg = "given value '#{value}' specified for option '#{argument_spec.name}' must be a positive integer"
158
+
159
+ raise IntegerOutOfRangeException, msg
160
+ end
161
+ when :non_positive
162
+
163
+ if v > 0
164
+
165
+ msg = "given value '#{value}' specified for option '#{argument_spec.name}' must be a non-positive integer"
166
+
167
+ raise IntegerOutOfRangeException, msg
168
+ end
169
+ when :non_negative
170
+
171
+ if v < 0
172
+
173
+ msg = "given value '#{value}' specified for option '#{argument_spec.name}' must be a non-negative integer"
174
+
175
+ raise IntegerOutOfRangeException, msg
176
+ end
177
+ when Range
178
+
179
+ unless range.include?
180
+
181
+ msg = "given value '#{value}' specified for option '#{argument_spec.name}' does not fall within the required range"
182
+
183
+ raise IntegerOutOfRangeException, msg
184
+ end
185
+ else
186
+
187
+ ;
188
+ end
189
+ end
190
+
191
+ v
192
+ end
193
+ end # module Internal_
194
+
195
+ def value_from_Proc(constraint, value, arg, given_index, given_name, argument_spec, extras)
196
+
197
+ value
198
+ end
199
+
200
+ def value_from_Hash(constraint, value, arg, given_index, given_name, argument_spec, extras)
201
+
202
+ # Check if type is specified; if not, String is assumed
203
+
204
+ type = constraint[:type]
205
+
206
+
207
+ if Internal_.is_integer?(type)
208
+
209
+ return Internal_.obtain_integer(value, constraint, argument_spec)
210
+ end
211
+
212
+
213
+ value
214
+ end
215
+ end # module ValueParser
216
+
217
+ end # module util
218
+ end # module CLASP
219
+
220
+ # ############################## end of file ############################# #
221
+
222
+
data/lib/clasp/version.rb CHANGED
@@ -5,12 +5,13 @@
5
5
  # Purpose: Version for CLASP.Ruby library
6
6
  #
7
7
  # Created: 16th November 2014
8
- # Updated: 20th April 2019
8
+ # Updated: 26th June 2022
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/CLASP.Ruby
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
+ # Copyright (c) 2019-2022, Matthew Wilson and Synesis Information Systems
14
15
  # Copyright (c) 2014-2019, Matthew Wilson and Synesis Software
15
16
  # All rights reserved.
16
17
  #
@@ -51,7 +52,7 @@
51
52
  module CLASP
52
53
 
53
54
  # Current version of the CLASP.Ruby library
54
- VERSION = '0.20.3'
55
+ VERSION = '0.22.1'
55
56
 
56
57
  private
57
58
  # @!visibility private
@@ -0,0 +1,310 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '../..', 'lib')
4
+
5
+ require 'clasp'
6
+
7
+ require 'xqsr3/extensions/test/unit' if RUBY_VERSION >= '2'
8
+
9
+ require 'test/unit'
10
+
11
+ class Test_TypedOptionValues < Test::Unit::TestCase
12
+
13
+ if RUBY_VERSION < '2'
14
+
15
+ def assert_raise_with_message(type_spec, message_spec, *args, &block)
16
+
17
+ assert_raise(type_spec, *args, &block)
18
+ end
19
+ end
20
+
21
+ def test_Integer_no_range
22
+
23
+ specifications = [
24
+
25
+ CLASP.Option('--level', default_value: 1234, constraint: { type: Integer })
26
+ ]
27
+
28
+ # with no arguments
29
+ begin
30
+
31
+ argv = []
32
+ args = CLASP.parse argv, specifications
33
+
34
+ assert_equal 0, args.flags.size
35
+ assert_equal 0, args.options.size
36
+ assert_equal 0, args.values.size
37
+ end
38
+
39
+ # with default value
40
+ begin
41
+
42
+ argv = [ '--level=' ]
43
+
44
+ args = CLASP.parse argv, specifications
45
+
46
+ assert_equal 0, args.flags.size
47
+ assert_equal 1, args.options.size
48
+ assert_equal 0, args.values.size
49
+
50
+ opt = args.options[0]
51
+
52
+ assert_equal 0, opt.given_index
53
+ assert_equal specifications[0], opt.argument_specification
54
+ assert_equal '--level', opt.name
55
+ assert_equal '', opt.given_value
56
+ assert_equal 1234, opt.value
57
+ end
58
+
59
+ # with explicit value 0
60
+ begin
61
+
62
+ argv = [ '--level=0' ]
63
+
64
+ args = CLASP.parse argv, specifications
65
+
66
+ assert_equal 0, args.flags.size
67
+ assert_equal 1, args.options.size
68
+ assert_equal 0, args.values.size
69
+
70
+ opt = args.options[0]
71
+
72
+ assert_equal 0, opt.given_index
73
+ assert_equal specifications[0], opt.argument_specification
74
+ assert_equal '--level', opt.name
75
+ assert_equal '0', opt.given_value
76
+ assert_equal 0, opt.value
77
+ end
78
+
79
+ # with explicit value -100
80
+ begin
81
+
82
+ argv = [ '--level=-100' ]
83
+
84
+ args = CLASP.parse argv, specifications
85
+
86
+ assert_equal 0, args.flags.size
87
+ assert_equal 1, args.options.size
88
+ assert_equal 0, args.values.size
89
+
90
+ opt = args.options[0]
91
+
92
+ assert_equal 0, opt.given_index
93
+ assert_equal specifications[0], opt.argument_specification
94
+ assert_equal '--level', opt.name
95
+ assert_equal '-100', opt.given_value
96
+ assert_equal -100, opt.value
97
+ end
98
+
99
+ # with explicit value 123456789
100
+ begin
101
+
102
+ argv = [ '--level=123456789' ]
103
+
104
+ args = CLASP.parse argv, specifications
105
+
106
+ assert_equal 0, args.flags.size
107
+ assert_equal 1, args.options.size
108
+ assert_equal 0, args.values.size
109
+
110
+ opt = args.options[0]
111
+
112
+ assert_equal 0, opt.given_index
113
+ assert_equal specifications[0], opt.argument_specification
114
+ assert_equal '--level', opt.name
115
+ assert_equal '123456789', opt.given_value
116
+ assert_equal 123456789, opt.value
117
+ end
118
+
119
+ # with explicit value +123456789
120
+ begin
121
+
122
+ argv = [ '--level=+123456789' ]
123
+
124
+ args = CLASP.parse argv, specifications
125
+
126
+ assert_equal 0, args.flags.size
127
+ assert_equal 1, args.options.size
128
+ assert_equal 0, args.values.size
129
+
130
+ opt = args.options[0]
131
+
132
+ assert_equal 0, opt.given_index
133
+ assert_equal specifications[0], opt.argument_specification
134
+ assert_equal '--level', opt.name
135
+ assert_equal '+123456789', opt.given_value
136
+ assert_equal 123456789, opt.value
137
+ end
138
+ end
139
+
140
+ def test_Integer_positive
141
+
142
+ specifications = [
143
+
144
+ CLASP.Option('--level', default_value: 1234, constraint: { type: Integer, range: :positive })
145
+ ]
146
+
147
+ # with no arguments
148
+ begin
149
+
150
+ argv = []
151
+ args = CLASP.parse argv, specifications
152
+
153
+ assert_equal 0, args.flags.size
154
+ assert_equal 0, args.options.size
155
+ assert_equal 0, args.values.size
156
+ end
157
+
158
+ # with default value
159
+ begin
160
+
161
+ argv = [ '--level=' ]
162
+
163
+ args = CLASP.parse argv, specifications
164
+
165
+ assert_equal 0, args.flags.size
166
+ assert_equal 1, args.options.size
167
+ assert_equal 0, args.values.size
168
+
169
+ opt = args.options[0]
170
+
171
+ assert_equal 0, opt.given_index
172
+ assert_equal specifications[0], opt.argument_specification
173
+ assert_equal '--level', opt.name
174
+ assert_equal '', opt.given_value
175
+ assert_equal 1234, opt.value
176
+ end
177
+
178
+ # with explicit value 0
179
+ begin
180
+
181
+ argv = [ '--level=0' ]
182
+
183
+ assert_raise_with_message(CLASP::Exceptions::IntegerOutOfRangeException, /\b0\b.*--level.*must be a positive integer/) { CLASP.parse argv, specifications }
184
+ end
185
+
186
+ # with explicit value -100
187
+ begin
188
+
189
+ argv = [ '--level=-100' ]
190
+
191
+ assert_raise_with_message(CLASP::Exceptions::IntegerOutOfRangeException, /-100\b.*--level.*must be a positive integer/) { CLASP.parse argv, specifications }
192
+ end
193
+
194
+ # with explicit value 123456789
195
+ begin
196
+
197
+ argv = [ '--level=123456789' ]
198
+
199
+ args = CLASP.parse argv, specifications
200
+
201
+ assert_equal 0, args.flags.size
202
+ assert_equal 1, args.options.size
203
+ assert_equal 0, args.values.size
204
+
205
+ opt = args.options[0]
206
+
207
+ assert_equal 0, opt.given_index
208
+ assert_equal specifications[0], opt.argument_specification
209
+ assert_equal '--level', opt.name
210
+ assert_equal '123456789', opt.given_value
211
+ assert_equal 123456789, opt.value
212
+ end
213
+
214
+ # with explicit value +123456789
215
+ begin
216
+
217
+ argv = [ '--level=+123456789' ]
218
+
219
+ args = CLASP.parse argv, specifications
220
+
221
+ assert_equal 0, args.flags.size
222
+ assert_equal 1, args.options.size
223
+ assert_equal 0, args.values.size
224
+
225
+ opt = args.options[0]
226
+
227
+ assert_equal 0, opt.given_index
228
+ assert_equal specifications[0], opt.argument_specification
229
+ assert_equal '--level', opt.name
230
+ assert_equal '+123456789', opt.given_value
231
+ assert_equal 123456789, opt.value
232
+ end
233
+ end
234
+
235
+ def test_Integer_range_values_range
236
+
237
+ specifications = [
238
+
239
+ CLASP.Option('--level', default_value: 1234, values_range: [ 1234, -1234, 7, 19 ], constraint: { type: Integer })
240
+ ]
241
+
242
+ # with no arguments
243
+ begin
244
+
245
+ argv = []
246
+ args = CLASP.parse argv, specifications
247
+
248
+ assert_equal 0, args.flags.size
249
+ assert_equal 0, args.options.size
250
+ assert_equal 0, args.values.size
251
+ end
252
+
253
+ # with default value
254
+ begin
255
+
256
+ argv = [ '--level=' ]
257
+
258
+ args = CLASP.parse argv, specifications
259
+
260
+ assert_equal 0, args.flags.size
261
+ assert_equal 1, args.options.size
262
+ assert_equal 0, args.values.size
263
+
264
+ opt = args.options[0]
265
+
266
+ assert_equal 0, opt.given_index
267
+ assert_equal specifications[0], opt.argument_specification
268
+ assert_equal '--level', opt.name
269
+ assert_equal '', opt.given_value
270
+ assert_equal 1234, opt.value
271
+ end
272
+
273
+ # with explicit value 0
274
+ begin
275
+
276
+ argv = [ '--level=0' ]
277
+
278
+ assert_raise_with_message(CLASP::Exceptions::IntegerOutOfRangeException, /\b0\b.*--level.*does not fall within the required range/) { CLASP.parse argv, specifications }
279
+ end
280
+
281
+ spec0 = specifications[0]
282
+ values = spec0.values_range.sort[0]..spec0.values_range.sort[-1]
283
+
284
+ values.each do |val|
285
+
286
+ argv = [ "--level=#{val}" ]
287
+
288
+ if spec0.values_range.include? val
289
+
290
+ args = CLASP.parse argv, specifications
291
+
292
+ assert_equal 0, args.flags.size
293
+ assert_equal 1, args.options.size
294
+ assert_equal 0, args.values.size
295
+
296
+ opt = args.options[0]
297
+
298
+ assert_equal 0, opt.given_index
299
+ assert_equal specifications[0], opt.argument_specification
300
+ assert_equal '--level', opt.name
301
+ assert_equal val.to_s, opt.given_value
302
+ assert_equal val, opt.value
303
+ else
304
+
305
+ assert_raise_with_message(CLASP::Exceptions::IntegerOutOfRangeException, /#{val}\b.*--level.*does not fall within the required range/) { CLASP.parse argv, specifications }
306
+ end
307
+ end
308
+ end
309
+ end
310
+
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '../..', 'lib')
4
+
5
+ require 'clasp'
6
+
7
+ require 'xqsr3/extensions/test/unit'
8
+
9
+ require 'test/unit'
10
+
11
+ class Test_WithAction < Test::Unit::TestCase
12
+
13
+ def test_flag_with_action
14
+
15
+ debug = false
16
+
17
+ specifications = [
18
+
19
+ CLASP.Flag('--debug', alias: '-d') { debug = true }
20
+ ]
21
+ argv = []
22
+ args = CLASP.parse argv, specifications
23
+
24
+ assert_equal 0, args.flags.size
25
+ assert_equal 0, args.options.size
26
+ assert_equal 0, args.values.size
27
+
28
+ assert_false debug
29
+
30
+ argv2 = [ '--debug' ]
31
+ args2 = CLASP.parse argv2, specifications
32
+
33
+ assert_equal 1, args2.flags.size
34
+ assert_equal 0, args2.options.size
35
+ assert_equal 0, args2.values.size
36
+
37
+ assert_false debug
38
+
39
+ if ix = args2.flags.index('--debug')
40
+
41
+ flag = args2.flags[ix]
42
+
43
+ flag.argument_specification.action.call(flag, flag.argument_specification)
44
+ end
45
+
46
+ assert_true debug
47
+ end
48
+ end
49
+
50
+
metadata CHANGED
@@ -1,72 +1,66 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clasp-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.3
5
- prerelease:
4
+ version: 0.22.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Matt Wilson
9
- autorequire:
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2019-04-20 00:00:00.000000000 Z
11
+ date: 2022-06-26 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: xqsr3
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
- version: '0.30'
19
+ version: '0.37'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ~>
24
+ - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: '0.30'
30
- description: ! 'Command-Line Argument Sorting and Parsing library that provides a
31
- powerful
32
-
33
- abstraction of command-line interpretation facilities. CLASP.Ruby is a Ruby port
34
- of the popular CLASP (C/C++) library, and provides declarative specification of
35
- command-line flags and options, aliasing, flag combination, UNIX de-facto standard
36
- flag processing, and a number of utility functions for expressing usage and version
37
- information.
38
-
39
- '
26
+ version: '0.37'
27
+ description: |
28
+ Command-Line Argument Sorting and Parsing library that provides a powerful
29
+ abstraction of command-line interpretation facilities. CLASP.Ruby is a Ruby port of the popular CLASP (C/C++) library, and provides declarative specification of command-line flags and options, aliasing, flag combination, UNIX de-facto standard flag processing, and a number of utility functions for expressing usage and version information.
40
30
  email: matthew@synesis.com.au
41
31
  executables: []
42
32
  extensions: []
43
33
  extra_rdoc_files: []
44
34
  files:
35
+ - LICENSE
36
+ - README.md
45
37
  - examples/cr-example.rb
46
38
  - examples/flag_and_option_specifications.md
47
39
  - examples/flag_and_option_specifications.rb
48
40
  - examples/show_usage_and_version.md
49
41
  - examples/show_usage_and_version.rb
50
42
  - examples/simple_command_line_no_specifications.rb
43
+ - lib/clasp-ruby.rb
44
+ - lib/clasp.rb
51
45
  - lib/clasp/arguments.rb
52
46
  - lib/clasp/clasp.rb
53
47
  - lib/clasp/cli.rb
54
48
  - lib/clasp/doc_.rb
55
49
  - lib/clasp/old_module.rb
56
50
  - lib/clasp/specifications.rb
51
+ - lib/clasp/util/exceptions.rb
52
+ - lib/clasp/util/value_parser.rb
57
53
  - lib/clasp/version.rb
58
- - lib/clasp-ruby.rb
59
- - lib/clasp.rb
60
54
  - test/scratch/test_list_command_line.rb
61
55
  - test/scratch/test_specifications.rb
62
56
  - test/scratch/test_usage.rb
63
57
  - test/scratch/test_usage_from_DATA.rb
64
58
  - test/scratch/test_usage_with_duplicate_specifications.rb
59
+ - test/unit/tc_ARGV_rewrite.rb
65
60
  - test/unit/tc_arguments_1.rb
66
61
  - test/unit/tc_arguments_2.rb
67
62
  - test/unit/tc_arguments_3.rb
68
63
  - test/unit/tc_arguments_inspect.rb
69
- - test/unit/tc_ARGV_rewrite.rb
70
64
  - test/unit/tc_cli.rb
71
65
  - test/unit/tc_default_value.rb
72
66
  - test/unit/tc_defaults_1.rb
@@ -75,33 +69,34 @@ files:
75
69
  - test/unit/tc_option_required.rb
76
70
  - test/unit/tc_option_value_aliases.rb
77
71
  - test/unit/tc_specifications.rb
72
+ - test/unit/tc_typed_options.rb
78
73
  - test/unit/tc_usage.rb
74
+ - test/unit/tc_with_action.rb
79
75
  - test/unit/ts_all.rb
80
- - README.md
81
- - LICENSE
82
76
  homepage: http://github.com/synesissoftware/CLASP.Ruby
83
77
  licenses:
84
78
  - BSD-3-Clause
85
- post_install_message:
79
+ metadata: {}
80
+ post_install_message:
86
81
  rdoc_options: []
87
82
  require_paths:
88
83
  - lib
89
84
  required_ruby_version: !ruby/object:Gem::Requirement
90
- none: false
91
85
  requirements:
92
- - - ! '>='
86
+ - - ">="
93
87
  - !ruby/object:Gem::Version
94
88
  version: 1.9.3
89
+ - - "<"
90
+ - !ruby/object:Gem::Version
91
+ version: '4'
95
92
  required_rubygems_version: !ruby/object:Gem::Requirement
96
- none: false
97
93
  requirements:
98
- - - ! '>='
94
+ - - ">="
99
95
  - !ruby/object:Gem::Version
100
96
  version: '0'
101
97
  requirements: []
102
- rubyforge_project:
103
- rubygems_version: 1.8.23.2
104
- signing_key:
105
- specification_version: 3
98
+ rubygems_version: 3.2.3
99
+ signing_key:
100
+ specification_version: 4
106
101
  summary: CLASP.Ruby
107
102
  test_files: []