clasp-ruby 0.20.3 → 0.22.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 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: []